* [9fans] v9fs - Add 'synthetic' mount option for streaming read support ?
@ 2026-02-08 8:37 kanze
2026-02-08 11:58 ` red
` (3 more replies)
0 siblings, 4 replies; 9+ messages in thread
From: kanze @ 2026-02-08 8:37 UTC (permalink / raw)
To: 9fans
[-- Attachment #1: Type: text/plain, Size: 1604 bytes --]
Linux v9fs does not support streaming reads from synthetic 9P filesystems.
When a synthetic file (e.g. an LLM output stream) blocks on Tread and returns data incrementally via short reads, Linux v9fs treats short reads as near-EOF and/or gates reads on i_size, causing `cat` to return the entire output in one block instead of streaming.
Plan 9 handles this correctly:
cat(1) loops on read(2), the kernel issues Tread, the server blocks until data is available, returns a short read, and cat prints it immediately.
On Linux, two things break this:
1. netfs_unbuffered_read_iter() and the underlying netfs DIO path treat short reads (submitted < len) as errors (EIO) rather than partial success.
2. The generic VFS read path consults i_size (from Rstat) to decide whether there is anything to read. Synthetic files often report i_size=0 because their content is generated on demand.
Inside netfs_unbuffered_read() (in fs/netfs/direct_read.c), after dispatching the read and waiting:
if (ret == 0 && rreq->submitted < rreq->len && rreq->origin != NETFS_DIO_READ)
{
trace_netfs_failure(rreq, NULL, ret, netfs_fail_short_read); ret = -EIO; // ← THIS KILLS STREAMING
}
The netfs library was written for AFS/CIFS/NFS where a short read means corruption, not "data is still being generated."
What do you guys think ? Am I missing something ?
------------------------------------------
9fans: 9fans
Permalink: https://9fans.topicbox.com/groups/9fans/T52da395428d9d9c7-Mc329e0b523b0d2d5a612ba21
Delivery options: https://9fans.topicbox.com/groups/9fans/subscription
[-- Attachment #2: Type: text/html, Size: 2484 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [9fans] v9fs - Add 'synthetic' mount option for streaming read support ?
2026-02-08 8:37 [9fans] v9fs - Add 'synthetic' mount option for streaming read support ? kanze
@ 2026-02-08 11:58 ` red
2026-02-08 13:19 ` Ori Bernstein
2026-02-08 12:01 ` Arusekk via 9fans
` (2 subsequent siblings)
3 siblings, 1 reply; 9+ messages in thread
From: red @ 2026-02-08 11:58 UTC (permalink / raw)
To: kanzari.e; +Cc: 9fans
> Linux v9fs does not support streaming reads from synthetic 9P filesystems.
linux v9fs is broken in many more ways than just
that.
> [...]
> What do you guys think ? Am I missing something ?
maybe im missing something.
why is this here, at all?
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [9fans] v9fs - Add 'synthetic' mount option for streaming read support ?
2026-02-08 8:37 [9fans] v9fs - Add 'synthetic' mount option for streaming read support ? kanze
2026-02-08 11:58 ` red
@ 2026-02-08 12:01 ` Arusekk via 9fans
2026-02-08 12:21 ` [9fans] " Alyssa M via 9fans
2026-02-08 12:38 ` kanze
3 siblings, 0 replies; 9+ messages in thread
From: Arusekk via 9fans @ 2026-02-08 12:01 UTC (permalink / raw)
To: 9fans, kanze
[-- Attachment #1: Type: text/plain, Size: 1883 bytes --]
Hi,
for my uglendix[1] project, I needed to make several patches for v9fs.
You can find them in the extras directory in the source tree.
[1]: https://uglendix.dev
Dnia 8 lutego 2026 09:37:59 CET, kanze <kanzari.e@gmail.com> napisał/a:
> Linux v9fs does not support streaming reads from synthetic 9P filesystems.
>
> When a synthetic file (e.g. an LLM output stream) blocks on Tread and returns data incrementally via short reads, Linux v9fs treats short reads as near-EOF and/or gates reads on i_size, causing `cat` to return the entire output in one block instead of streaming.
> Plan 9 handles this correctly:
> cat(1) loops on read(2), the kernel issues Tread, the server blocks until data is available, returns a short read, and cat prints it immediately.
> On Linux, two things break this:
> 1. netfs_unbuffered_read_iter() and the underlying netfs DIO path treat short reads (submitted < len) as errors (EIO) rather than partial success.
> 2. The generic VFS read path consults i_size (from Rstat) to decide whether there is anything to read. Synthetic files often report i_size=0 because their content is generated on demand.
>
> Inside netfs_unbuffered_read() (in fs/netfs/direct_read.c), after dispatching the read and waiting:
>
> if (ret == 0 && rreq->submitted < rreq->len && rreq->origin != NETFS_DIO_READ)
> {
> trace_netfs_failure(rreq, NULL, ret, netfs_fail_short_read); ret = -EIO; // ← THIS KILLS STREAMING
> }
>
> The netfs library was written for AFS/CIFS/NFS where a short read means corruption, not "data is still being generated."
>
> What do you guys think ? Am I missing something ?
------------------------------------------
9fans: 9fans
Permalink: https://9fans.topicbox.com/groups/9fans/T52da395428d9d9c7-M8baf3d1a3b91c9cb16b16479
Delivery options: https://9fans.topicbox.com/groups/9fans/subscription
[-- Attachment #2: Type: text/html, Size: 3033 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* [9fans] Re: v9fs - Add 'synthetic' mount option for streaming read support ?
2026-02-08 8:37 [9fans] v9fs - Add 'synthetic' mount option for streaming read support ? kanze
2026-02-08 11:58 ` red
2026-02-08 12:01 ` Arusekk via 9fans
@ 2026-02-08 12:21 ` Alyssa M via 9fans
2026-02-08 12:38 ` kanze
3 siblings, 0 replies; 9+ messages in thread
From: Alyssa M via 9fans @ 2026-02-08 12:21 UTC (permalink / raw)
To: 9fans
[-- Attachment #1: Type: text/plain, Size: 505 bytes --]
I think linux cat(1) does its own buffering, plan9 cat(1) does not - it does one write for every read.
If I try it with:
$ dd bs=1 <remote-9p-device-file-over-v9fs
v9fs seems quite happy to return data over 9p in the sizes it's sent.
Maybe I'm misunderstanding your issue?
------------------------------------------
9fans: 9fans
Permalink: https://9fans.topicbox.com/groups/9fans/T52da395428d9d9c7-M533cdce2aae15a3bd7d7dc89
Delivery options: https://9fans.topicbox.com/groups/9fans/subscription
[-- Attachment #2: Type: text/html, Size: 1286 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* [9fans] Re: v9fs - Add 'synthetic' mount option for streaming read support ?
2026-02-08 8:37 [9fans] v9fs - Add 'synthetic' mount option for streaming read support ? kanze
` (2 preceding siblings ...)
2026-02-08 12:21 ` [9fans] " Alyssa M via 9fans
@ 2026-02-08 12:38 ` kanze
2026-02-08 14:30 ` Alyssa M via 9fans
3 siblings, 1 reply; 9+ messages in thread
From: kanze @ 2026-02-08 12:38 UTC (permalink / raw)
To: 9fans
[-- Attachment #1: Type: text/plain, Size: 8030 bytes --]
Not a cat issue, tried with custom read, i.e. :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#define BUFSIZE 8192
static void
cat(int fd, const char *name)
{
char buf[BUFSIZE];
ssize_t n, written, total;
int flags;
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
while (1) {
n = read(fd, buf, sizeof(buf));
if (n > 0) {
/* Got data - write it immediately */
total = 0;
while (total < n) {
written = write(STDOUT_FILENO, buf + total, n - total);
if (written < 0) {
if (errno == EINTR)
continue;
fprintf(stderr, "v9cat: write error: %s\n", strerror(errno));
exit(1);
}
total += written;
}
} else if (n == 0) {
/* EOF */
break;
} else {
/* n < 0 - error or would block */
if (errno == EAGAIN || errno == EWOULDBLOCK) {
/* No data available yet - wait a tiny bit */
usleep(1000); /* 1ms */
continue;
} else if (errno == EINTR) {
continue;
} else {
fprintf(stderr, "v9cat: error reading %s: %s\n",
name, strerror(errno));
exit(1);
}
}
}
}
int
main(int argc, char *argv[])
{
int fd;
int i;
if (argc == 1) {
cat(STDIN_FILENO, "<stdin>");
} else {
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-' && argv[i][1] == '\0') {
cat(STDIN_FILENO, "<stdin>");
continue;
}
/*
* Open with O_NONBLOCK so v9fs returns data immediately
* instead of waiting to fill the buffer
*/
fd = open(argv[i], O_RDONLY | O_NONBLOCK);
if (fd < 0) {
fprintf(stderr, "v9cat: can't open %s: %s\n",
argv[i], strerror(errno));
exit(1);
}
cat(fd, argv[i]);
close(fd);
}
}
return 0;
}
Quick fix for those interested (will work with linux cat(1)):
---
fs/9p/v9fs.c | 9 ++++++
fs/9p/v9fs.h | 3 ++
fs/9p/vfs_file.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++
fs/9p/vfs_super.c| 6 ++++
4 files changed, 96 insertions(+)
FILE: fs/9p/v9fs.h — Add V9FS_SYNTHETIC flag and session field
The session_info structure tracks per-mount options. We add a flag so
every read path can check if this mount is synthetic.
--- a/fs/9p/v9fs.h
+++ b/fs/9p/v9fs.h
@@ -35,6 +35,7 @@
/* Flags */
#define V9FS_NO_XATTR 0x10
+#define V9FS_SYNTHETIC 0x20 /* Synthetic FS: streaming reads, ignore i_size */
/* possible values of ->cache */
/**
@@ -61,6 +62,8 @@ struct v9fs_session_info {
struct p9_client *clnt; /* 9p client */
struct list_head slist; /* list of sessions registered with v9fs */
struct rw_semaphore rename_sem;
+ /* Set by 'synthetic' mount option for streaming read support */
+ unsigned int synthetic:1;
long session_lock_timeout; /* retry interval for blocking locks */
};
FILE: fs/9p/v9fs.c — Parse the 'synthetic' mount option
Add "synthetic" to the option parser enum and match table.
--- a/fs/9p/v9fs.c
+++ b/fs/9p/v9fs.c
@@ -55,6 +55,7 @@ enum {
Opt_cache, Opt_cachetag,
/* Access options */
Opt_access, Opt_posixacl,
+ Opt_synthetic,
/* Lock timeout option */
Opt_locktimeout,
/* Error token */
@@ -75,6 +76,7 @@ static const match_table_t tokens = {
{Opt_cachetag, "cachetag=%s"},
{Opt_access, "access=%s"},
{Opt_posixacl, "posixacl"},
+ {Opt_synthetic, "synthetic"},
{Opt_locktimeout, "locktimeout=%u"},
{Opt_err, NULL}
};
@@ -200,6 +202,13 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
#endif
break;
+ case Opt_synthetic:
+ v9ses->synthetic = 1;
+ /* Synthetic implies no caching */
+ v9ses->cache = CACHE_NONE;
+ v9ses->flags |= V9FS_SYNTHETIC;
+ break;
+
case Opt_locktimeout:
r = match_int(&args[0], &option);
if (r < 0) {
FILE: fs/9p/vfs_file.c — Bypass netfs for reads on synthetic mounts
Instead of delegating to netfs (which treats short
reads as EIO), we do direct p9_client_read_once() calls that return
partial data immediately.
--- a/fs/9p/vfs_file.c
+++ b/fs/9p/vfs_file.c
@@ -21,6 +21,75 @@
#include "fid.h"
#include "cache.h"
+/**
+ * v9fs_synthetic_read - Direct streaming read bypassing netfs
+ * @iocb: I/O control block
+ * @to: destination iterator
+ *
+ * For synthetic 9P filesystems, we need Plan 9 read semantics:
+ * - Issue a single Tread to the server
+ * - Server blocks until data is available
+ * - Return whatever the server gives us (short read = partial data)
+ * - Return 0 only when the server returns 0 (true EOF)
+ *
+ * This bypasses the netfs layer entirely because netfs:
+ * 1. Treats short reads (submitted < requested) as EIO
+ * 2. Uses i_size to gate whether reads proceed at all
+ * 3. May coalesce/readahead, breaking blocking semantics
+ *
+ * The result is that `cat /mnt/foo/output` streams
+ * data incrementally, identical to `cat /n/foo/output`
+ * on Plan 9.
+ */
+static ssize_t
+v9fs_synthetic_read(struct kiocb *iocb, struct iov_iter *to)
+{
+ struct file *filp = iocb->ki_filp;
+ struct p9_fid *fid = filp->private_data;
+ int ret, err;
+
+ p9_debug(P9_DEBUG_VFS, "synthetic read: fid %d count %zu offset %lld\n",
+ fid->fid, iov_iter_count(to), iocb->ki_pos);
+
+ if (!iov_iter_count(to))
+ return 0;
+
+ /*
+ * p9_client_read_once() issues exactly one Tread.
+ * If the server blocks (data not yet available), we block here.
+ * When the server responds:
+ * - count > 0: partial data available, return it immediately
+ * - count == 0: EOF, return 0
+ * - err != 0: error, return error
+ *
+ * We do NOT loop to fill the buffer (that's what p9_client_read()
+ * does, and it would defeat streaming). One Tread, one response,
+ * one return to userspace.
+ */
+ ret = p9_client_read_once(fid, iocb->ki_pos, to, &err);
+ if (err)
+ return err;
+
+ iocb->ki_pos += ret;
+
+ /*
+ * Do NOT update i_size here. For synthetic files, i_size is
+ * meaningless — the file has no fixed length. Updating it would
+ * confuse subsequent reads and stat calls.
+ */
+
+ return ret;
+}
+
+
static const struct vm_operations_struct v9fs_mmap_file_vm_ops;
/*
@@ -xxx,12 +xxx,21 @@ static ssize_t
v9fs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
struct p9_fid *fid = iocb->ki_filp->private_data;
+ struct inode *inode = file_inode(iocb->ki_filp);
+ struct v9fs_session_info *v9ses;
+
p9_debug(P9_DEBUG_VFS, "fid %d count %zu offset %lld\n",
fid->fid, iov_iter_count(to), iocb->ki_pos);
+
+ v9ses = v9fs_inode2v9ses(inode);
+ if (v9ses->synthetic)
+ return v9fs_synthetic_read(iocb, to);
+
if (fid->mode & P9L_DIRECT)
return netfs_unbuffered_read_iter(iocb, to);
p9_debug(P9_DEBUG_VFS, "(cached)\n");
return netfs_file_read_iter(iocb, to);
}
FILE: fs/9p/vfs_super.c — Disable readahead for synthetic mounts
Even though synthetic implies cache=none (which already sets ra_pages=0),
we can make this explicit and document it.
--- a/fs/9p/vfs_super.c
+++ b/fs/9p/vfs_super.c
@@ -xxx,6 +xxx,12 @@ static int v9fs_fill_super(struct super_block *sb)
if (!v9ses->cache) {
sb->s_bdi->ra_pages = 0;
sb->s_bdi->io_pages = 0;
+ }
+
+ /* Synthetic mounts: force no readahead regardless of cache setting */
+ if (v9ses->flags & V9FS_SYNTHETIC) {
+ sb->s_bdi->ra_pages = 0;
+ sb->s_bdi->io_pages = 0;
} else {
sb->s_bdi->ra_pages = v9ses->maxdata >> PAGE_SHIFT;
sb->s_bdi->io_pages = v9ses->maxdata >> PAGE_SHIFT;
------------------------------------------
9fans: 9fans
Permalink: https://9fans.topicbox.com/groups/9fans/T52da395428d9d9c7-Mff613bbd3c47bfeaa501138b
Delivery options: https://9fans.topicbox.com/groups/9fans/subscription
[-- Attachment #2: Type: text/html, Size: 13554 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [9fans] v9fs - Add 'synthetic' mount option for streaming read support ?
2026-02-08 11:58 ` red
@ 2026-02-08 13:19 ` Ori Bernstein
2026-02-08 17:44 ` red
0 siblings, 1 reply; 9+ messages in thread
From: Ori Bernstein @ 2026-02-08 13:19 UTC (permalink / raw)
To: 9fans; +Cc: red, kanzari.e
On Sun, 8 Feb 2026 11:58:43 +0000
red <red@redfrey.com> wrote:
> > Linux v9fs does not support streaming reads from synthetic 9P filesystems.
>
> linux v9fs is broken in many more ways than just
> that.
I don't think this is a very helpful response to someone
trying to fix it; at least enumerating a few examples of
known bugs would be useful.
> > [...]
> > What do you guys think ? Am I missing something ?
>
> maybe im missing something.
>
> why is this here, at all?
>
because they're looking to get feedback from people that
are familiar with 9p, I assume. Fixing linux 9p interop
is definitely on topic.
--
Ori Bernstein <ori@eigenstate.org>
------------------------------------------
9fans: 9fans
Permalink: https://9fans.topicbox.com/groups/9fans/T52da395428d9d9c7-Mdad1d491f932001f1f2fb542
Delivery options: https://9fans.topicbox.com/groups/9fans/subscription
^ permalink raw reply [flat|nested] 9+ messages in thread
* [9fans] Re: v9fs - Add 'synthetic' mount option for streaming read support ?
2026-02-08 12:38 ` kanze
@ 2026-02-08 14:30 ` Alyssa M via 9fans
0 siblings, 0 replies; 9+ messages in thread
From: Alyssa M via 9fans @ 2026-02-08 14:30 UTC (permalink / raw)
To: 9fans
[-- Attachment #1: Type: text/plain, Size: 954 bytes --]
Oh! I *was* misunderstanding!
Yes this would seem to be an issue.
Reading bytes one at a time as I was doing with dd(1) works, but would probably be slow if you're doing a lot of it.
I have wondered in the past whether it would be good for 9p to mark files with an attribute bit if they are unseekable (synthetic). It seems a shame to apply such a drastic caching/readahead constraint to the whole mount. As it is one can guess that if you're asked to read a zero-sized file it might be a (potentially blocking) device. We used to have IFCHR... 8-). While I understand the desire to make everything uniform, sometimes one has to know... (I can see this going over like a lead balloon :-)...) Perhaps there's a better way?
------------------------------------------
9fans: 9fans
Permalink: https://9fans.topicbox.com/groups/9fans/T52da395428d9d9c7-M7f4eb17cca8fb601e26c6fc0
Delivery options: https://9fans.topicbox.com/groups/9fans/subscription
[-- Attachment #2: Type: text/html, Size: 1520 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [9fans] v9fs - Add 'synthetic' mount option for streaming read support ?
2026-02-08 13:19 ` Ori Bernstein
@ 2026-02-08 17:44 ` red
2026-02-09 15:29 ` ron minnich
0 siblings, 1 reply; 9+ messages in thread
From: red @ 2026-02-08 17:44 UTC (permalink / raw)
To: ori; +Cc: 9fans, kanzari.e, red
> because they're looking to get feedback from people that
> are familiar with 9p, I assume. Fixing linux 9p interop
> is definitely on topic.
the problem is that v9fs operates on
different assumptions than 9p itself
i cannot say how v9fs behaved prior to
netfs, nor what problems it tried to
fix, but using netfs for 9p is simply
wrong.
as kanze already pointed out, netfs was
designed around afs/cifs/nfs semantics,
and that makes correct 9p behavior the
exception, not the rule
if we're talking about 9p interop, my
suggestion is to rewrite v9fs instead of
incrementally patching edge cases until
it happens to exhibit correct behavior
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [9fans] v9fs - Add 'synthetic' mount option for streaming read support ?
2026-02-08 17:44 ` red
@ 2026-02-09 15:29 ` ron minnich
0 siblings, 0 replies; 9+ messages in thread
From: ron minnich @ 2026-02-09 15:29 UTC (permalink / raw)
To: 9fans; +Cc: ori, kanzari.e, red
[-- Attachment #1: Type: text/plain, Size: 1682 bytes --]
I wrote the first v9fs, in 1998, and I can tell you that what's in there
now is far different. What I wrote came before the dentry layer was
created, and it was possible back then to match linux vfs to what 9p had to
offer.
As linux has worked to optimize nfs, and the other network file systems, it
has made it harder for 9p to work well. FID to dnode management has been a
particular pain point. And other things happened too: for 18 months, Linux
9p was broken such that you could not open a file more than once. Not
kidding. To this day, on many linux kernels, you are allowed to Twalk an
open file -- there's no changing that.
I don't expect this to get better.
On Sun, Feb 8, 2026 at 1:02 PM red <red@redfrey.com> wrote:
> > because they're looking to get feedback from people that
> > are familiar with 9p, I assume. Fixing linux 9p interop
> > is definitely on topic.
>
> the problem is that v9fs operates on
> different assumptions than 9p itself
>
> i cannot say how v9fs behaved prior to
> netfs, nor what problems it tried to
> fix, but using netfs for 9p is simply
> wrong.
>
> as kanze already pointed out, netfs was
> designed around afs/cifs/nfs semantics,
> and that makes correct 9p behavior the
> exception, not the rule
>
> if we're talking about 9p interop, my
> suggestion is to rewrite v9fs instead of
> incrementally patching edge cases until
> it happens to exhibit correct behavior
>
------------------------------------------
9fans: 9fans
Permalink: https://9fans.topicbox.com/groups/9fans/T52da395428d9d9c7-M55c66b3cf53a1848ce9e395b
Delivery options: https://9fans.topicbox.com/groups/9fans/subscription
[-- Attachment #2: Type: text/html, Size: 2571 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-02-09 16:36 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-08 8:37 [9fans] v9fs - Add 'synthetic' mount option for streaming read support ? kanze
2026-02-08 11:58 ` red
2026-02-08 13:19 ` Ori Bernstein
2026-02-08 17:44 ` red
2026-02-09 15:29 ` ron minnich
2026-02-08 12:01 ` Arusekk via 9fans
2026-02-08 12:21 ` [9fans] " Alyssa M via 9fans
2026-02-08 12:38 ` kanze
2026-02-08 14:30 ` Alyssa M via 9fans
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).