* [PATCH] fuse: honor desc offset in readahead reads
@ 2026-07-16 9:40 Haofeng Li
2026-07-16 18:11 ` Joanne Koong
0 siblings, 1 reply; 8+ messages in thread
From: Haofeng Li @ 2026-07-16 9:40 UTC (permalink / raw)
To: miklos
Cc: fuse-devel, linux-kernel, joannelkoong, djwong, brauner, Haofeng Li
fuse_handle_readahead records non-zero descs[].offset when
iomap skips leading uptodate blocks in a folio, but
fuse_send_readpages built FUSE_READ from folio_pos() alone.
The reply was still copied at descs[0].offset, so wrong
file data was placed into the page cache.
Add descs[0].offset to the request position. Apply the
same correction in fuse_short_read for EOF size updates.
Fixes: 4ea907108a5c ("fuse: use iomap for readahead")
Signed-off-by: Haofeng Li <lihaofeng@kylinos.cn>
---
fs/fuse/file.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index ceada75310b8..11272983c991 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -836,7 +836,8 @@ static void fuse_short_read(struct inode *inode, u64 attr_ver, size_t num_read,
* reached the client fs yet. So the hole is not present there.
*/
if (!fc->writeback_cache) {
- loff_t pos = folio_pos(ap->folios[0]) + num_read;
+ loff_t pos = folio_pos(ap->folios[0]) +
+ ap->descs[0].offset + num_read;
fuse_read_update_size(inode, pos, attr_ver);
}
}
@@ -1057,7 +1058,8 @@ static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file,
struct fuse_file *ff = file->private_data;
struct fuse_mount *fm = ff->fm;
struct fuse_args_pages *ap = &ia->ap;
- loff_t pos = folio_pos(ap->folios[0]);
+ loff_t pos = folio_pos(ap->folios[0]) +
+ ap->descs[0].offset;
ssize_t res;
int err;
--
2.25.1
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] fuse: honor desc offset in readahead reads
2026-07-16 9:40 [PATCH] fuse: honor desc offset in readahead reads Haofeng Li
@ 2026-07-16 18:11 ` Joanne Koong
2026-07-17 3:03 ` Haofeng Li
2026-07-17 3:28 ` [PATCH v2] " Haofeng Li
0 siblings, 2 replies; 8+ messages in thread
From: Joanne Koong @ 2026-07-16 18:11 UTC (permalink / raw)
To: Haofeng Li; +Cc: miklos, fuse-devel, linux-kernel, djwong, brauner
On Thu, Jul 16, 2026 at 2:41 AM Haofeng Li <lihaofeng@kylinos.cn> wrote:
>
> fuse_handle_readahead records non-zero descs[].offset when
> iomap skips leading uptodate blocks in a folio, but
> fuse_send_readpages built FUSE_READ from folio_pos() alone.
> The reply was still copied at descs[0].offset, so wrong
> file data was placed into the page cache.
>
> Add descs[0].offset to the request position. Apply the
> same correction in fuse_short_read for EOF size updates.
>
> Fixes: 4ea907108a5c ("fuse: use iomap for readahead")
> Signed-off-by: Haofeng Li <lihaofeng@kylinos.cn>
> ---
> fs/fuse/file.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/fs/fuse/file.c b/fs/fuse/file.c
> index ceada75310b8..11272983c991 100644
> --- a/fs/fuse/file.c
> +++ b/fs/fuse/file.c
> @@ -836,7 +836,8 @@ static void fuse_short_read(struct inode *inode, u64 attr_ver, size_t num_read,
> * reached the client fs yet. So the hole is not present there.
> */
> if (!fc->writeback_cache) {
> - loff_t pos = folio_pos(ap->folios[0]) + num_read;
> + loff_t pos = folio_pos(ap->folios[0]) +
> + ap->descs[0].offset + num_read;
> fuse_read_update_size(inode, pos, attr_ver);
> }
> }
> @@ -1057,7 +1058,8 @@ static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file,
> struct fuse_file *ff = file->private_data;
> struct fuse_mount *fm = ff->fm;
> struct fuse_args_pages *ap = &ia->ap;
> - loff_t pos = folio_pos(ap->folios[0]);
> + loff_t pos = folio_pos(ap->folios[0]) +
> + ap->descs[0].offset;
nit: is this newline needed?
> ssize_t res;
> int err>
LGTM. Thanks for catching this.
Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] fuse: honor desc offset in readahead reads
2026-07-16 18:11 ` Joanne Koong
@ 2026-07-17 3:03 ` Haofeng Li
2026-07-17 3:28 ` [PATCH v2] " Haofeng Li
1 sibling, 0 replies; 8+ messages in thread
From: Haofeng Li @ 2026-07-17 3:03 UTC (permalink / raw)
To: joannelkoong
Cc: miklos, fuse-devel, linux-kernel, djwong, brauner, Haofeng Li
On Thu, 16 Jul 2026 11:11:10 -0700, Joanne Koong wrote:
> nit: is this newline needed?
>
> LGTM. Thanks for catching this.
>
> Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
Thanks for the review. I'll remove the newline in v2 and keep your Reviewed-by tag, as the logic stays the same.
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2] fuse: honor desc offset in readahead reads
2026-07-16 18:11 ` Joanne Koong
2026-07-17 3:03 ` Haofeng Li
@ 2026-07-17 3:28 ` Haofeng Li
2026-07-17 8:56 ` Miklos Szeredi
1 sibling, 1 reply; 8+ messages in thread
From: Haofeng Li @ 2026-07-17 3:28 UTC (permalink / raw)
To: miklos
Cc: joannelkoong, fuse-devel, linux-kernel, djwong, brauner, Haofeng Li
fuse_handle_readahead records non-zero descs[].offset when
iomap skips leading uptodate blocks in a folio, but
fuse_send_readpages built FUSE_READ from folio_pos() alone.
The reply was still copied at descs[0].offset, so wrong
file data was placed into the page cache.
Add descs[0].offset to the request position. Apply the
same correction in fuse_short_read for EOF size updates.
Fixes: 4ea907108a5c ("fuse: use iomap for readahead")
Signed-off-by: Haofeng Li <lihaofeng@kylinos.cn>
Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
---
Changes in v2:
- Collapse the split line in fuse_send_readpages back into one line.
- Add Joanne's Reviewed-by tag.
---
fs/fuse/file.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index ceada75310b8..9718a0353d26 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -836,7 +836,8 @@ static void fuse_short_read(struct inode *inode, u64 attr_ver, size_t num_read,
* reached the client fs yet. So the hole is not present there.
*/
if (!fc->writeback_cache) {
- loff_t pos = folio_pos(ap->folios[0]) + num_read;
+ loff_t pos = folio_pos(ap->folios[0]) +
+ ap->descs[0].offset + num_read;
fuse_read_update_size(inode, pos, attr_ver);
}
}
@@ -1057,7 +1058,7 @@ static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file,
struct fuse_file *ff = file->private_data;
struct fuse_mount *fm = ff->fm;
struct fuse_args_pages *ap = &ia->ap;
- loff_t pos = folio_pos(ap->folios[0]);
+ loff_t pos = folio_pos(ap->folios[0]) + ap->descs[0].offset;
ssize_t res;
int err;
--
2.25.1
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] fuse: honor desc offset in readahead reads
2026-07-17 3:28 ` [PATCH v2] " Haofeng Li
@ 2026-07-17 8:56 ` Miklos Szeredi
2026-07-17 10:17 ` Horst Birthelmer
2026-07-17 18:37 ` Haofeng Li
0 siblings, 2 replies; 8+ messages in thread
From: Miklos Szeredi @ 2026-07-17 8:56 UTC (permalink / raw)
To: Haofeng Li; +Cc: joannelkoong, fuse-devel, linux-kernel, djwong, brauner
On Fri, 17 Jul 2026 at 05:28, Haofeng Li <lihaofeng@kylinos.cn> wrote:
>
> fuse_handle_readahead records non-zero descs[].offset when
> iomap skips leading uptodate blocks in a folio, but
> fuse_send_readpages built FUSE_READ from folio_pos() alone.
> The reply was still copied at descs[0].offset, so wrong
> file data was placed into the page cache.
>
> Add descs[0].offset to the request position. Apply the
> same correction in fuse_short_read for EOF size updates.
>
> Fixes: 4ea907108a5c ("fuse: use iomap for readahead")
> Signed-off-by: Haofeng Li <lihaofeng@kylinos.cn>
> Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
Sashiko founds some more cases:
https://sashiko.dev/#/patchset/20260717032835.922433-1-lihaofeng%40kylinos.cn
This is a large folio issue, right? I do wonder why fsx-linux missed
these with the large folios enabled.
Thanks,
Miklos
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Re: [PATCH v2] fuse: honor desc offset in readahead reads
2026-07-17 8:56 ` Miklos Szeredi
@ 2026-07-17 10:17 ` Horst Birthelmer
2026-07-17 20:33 ` Joanne Koong
2026-07-17 18:37 ` Haofeng Li
1 sibling, 1 reply; 8+ messages in thread
From: Horst Birthelmer @ 2026-07-17 10:17 UTC (permalink / raw)
To: Miklos Szeredi
Cc: Haofeng Li, joannelkoong, fuse-devel, linux-kernel, djwong, brauner
On Fri, Jul 17, 2026 at 10:56:20AM +0200, Miklos Szeredi wrote:
> On Fri, 17 Jul 2026 at 05:28, Haofeng Li <lihaofeng@kylinos.cn> wrote:
> >
> > fuse_handle_readahead records non-zero descs[].offset when
> > iomap skips leading uptodate blocks in a folio, but
> > fuse_send_readpages built FUSE_READ from folio_pos() alone.
> > The reply was still copied at descs[0].offset, so wrong
> > file data was placed into the page cache.
> >
> > Add descs[0].offset to the request position. Apply the
> > same correction in fuse_short_read for EOF size updates.
> >
> > Fixes: 4ea907108a5c ("fuse: use iomap for readahead")
> > Signed-off-by: Haofeng Li <lihaofeng@kylinos.cn>
> > Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
>
> Sashiko founds some more cases:
> https://sashiko.dev/#/patchset/20260717032835.922433-1-lihaofeng%40kylinos.cn
>
> This is a large folio issue, right? I do wonder why fsx-linux missed
> these with the large folios enabled.
I think it probably never occured since fuse_readahead() only fills in folio boundaries?
This could be coincidence or readahead controls 'job'. I'm not that familiar with that code.
I just got suspicious on your question, since I have large folios enabled in testing, too, and
have not seen the case.
>
> Thanks,
> Miklos
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] fuse: honor desc offset in readahead reads
2026-07-17 8:56 ` Miklos Szeredi
2026-07-17 10:17 ` Horst Birthelmer
@ 2026-07-17 18:37 ` Haofeng Li
1 sibling, 0 replies; 8+ messages in thread
From: Haofeng Li @ 2026-07-17 18:37 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: Joanne Koong, fuse-devel, linux-kernel, djwong, brauner
Hi Miklos,
Thanks for the pointer. I looked into the cases raised by Sashiko, and
all three findings are valid.
More precisely, these are partial-folio rather than large-folio-specific
issues. The relevant condition is that a folio contains multiple
filesystem blocks. Large folios are one way to reach that condition,
but fuseblk with blksize < PAGE_SIZE is another.
On the current tree, regular FUSE and virtiofs use PAGE_SIZE as the
filesystem block size, and FUSE does not opt its mappings into large
folio allocation with mapping_set_large_folios(). Therefore, with
order-0 folios, iomap_adjust_read_range() cannot skip leading blocks and
produce a non-zero descriptor offset. Unless the test tree enabled
large folios for FUSE separately, this likely explains why fsx-linux did
not exercise this path. fsx-linux does verify data contents, so it
should detect the corruption if the partial-folio state is reached.
The currently reachable in-tree case is fuseblk with blksize < PAGE_SIZE,
after commit 93570c652b80 ("fuse: remove fc->blkbits workaround for
partial writes"). A partial buffered write can leave leading blocks
uptodate while later blocks still require readahead.
The three findings are:
1. In the /dev/fuse copy path, fuse_copy_folio() zeroes the entire folio
when page_zeroing is set and count < folio_size(). For a partial-folio
descriptor, this destroys the leading uptodate or dirty blocks that
iomap intentionally skipped. It should zero only the unfilled
descriptor range:
[offset + count, offset + length)
2. The virtiofs scatterlist starts at descs[i].offset, but its short-read
completion zeroes the absolute range [len, thislen). For a non-zero
descriptor offset, the correct range is:
[offset + len, offset + thislen)
The existing WARN_ON(descs[i].offset) reflects the old zero-offset
assumption, which is no longer valid for partial-folio reads.
3. At LLONG_MAX, fuse_send_readpages() decrements both the request count
and the last descriptor length. iomap has already accounted the
original descriptor length, while fuse_readpages_end() completes only
the decremented length. With an iomap_folio_state,
read_bytes_pending is left at one and the folio is never unlocked.
This affects both values of the async argument, since both completion
paths go through fuse_readpages_end(). The boundary is narrow, but it
is deterministically testable by extending the generic/525 scenario
to a multi-block folio configuration. The transfer count may be
reduced, but the original descriptor length needs to be retained for
iomap completion.
These issues predate this patch: non-zero descriptor offsets were already
recorded and used on the reply side. This patch fixes the independent
request-position mismatch so that the requested file range agrees with
the descriptor offset; it does not change descriptor construction or the
copy and completion handling above.
I will prepare the three additional fixes as separate patches. I can
either send them as follow-ups or respin this patch as part of a series,
whichever you prefer.
Thanks,
Haofeng
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Re: [PATCH v2] fuse: honor desc offset in readahead reads
2026-07-17 10:17 ` Horst Birthelmer
@ 2026-07-17 20:33 ` Joanne Koong
0 siblings, 0 replies; 8+ messages in thread
From: Joanne Koong @ 2026-07-17 20:33 UTC (permalink / raw)
To: Horst Birthelmer
Cc: Miklos Szeredi, Haofeng Li, fuse-devel, linux-kernel, djwong, brauner
On Fri, Jul 17, 2026 at 3:17 AM Horst Birthelmer <horst@birthelmer.de> wrote:
>
> On Fri, Jul 17, 2026 at 10:56:20AM +0200, Miklos Szeredi wrote:
> > On Fri, 17 Jul 2026 at 05:28, Haofeng Li <lihaofeng@kylinos.cn> wrote:
> > >
> > > fuse_handle_readahead records non-zero descs[].offset when
> > > iomap skips leading uptodate blocks in a folio, but
> > > fuse_send_readpages built FUSE_READ from folio_pos() alone.
> > > The reply was still copied at descs[0].offset, so wrong
> > > file data was placed into the page cache.
> > >
> > > Add descs[0].offset to the request position. Apply the
> > > same correction in fuse_short_read for EOF size updates.
> > >
> > > Fixes: 4ea907108a5c ("fuse: use iomap for readahead")
> > > Signed-off-by: Haofeng Li <lihaofeng@kylinos.cn>
> > > Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
> >
> > Sashiko founds some more cases:
> > https://sashiko.dev/#/patchset/20260717032835.922433-1-lihaofeng%40kylinos.cn
> >
> > This is a large folio issue, right? I do wonder why fsx-linux missed
What is fsx-linux?
> > these with the large folios enabled.
>
> I think it probably never occured since fuse_readahead() only fills in folio boundaries?
> This could be coincidence or readahead controls 'job'. I'm not that familiar with that code.
>
> I just got suspicious on your question, since I have large folios enabled in testing, too, and
> have not seen the case.
>
Looking more at this, I don't think this problem is actually
reachable. Haofeng, can you describe how you hit this issue?
This situation is only reached if there are prior uptodate blocks in
the folio. But looking at the filemap_get_pages() code in
mm/filemap.c, readahead is only called when handling first time reads
or speculatively prefetching new folios. afaict, there's no situation
where readahead gets called on a folio already in the page cache.
Thanks,
Joanne
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-07-17 20:33 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-16 9:40 [PATCH] fuse: honor desc offset in readahead reads Haofeng Li
2026-07-16 18:11 ` Joanne Koong
2026-07-17 3:03 ` Haofeng Li
2026-07-17 3:28 ` [PATCH v2] " Haofeng Li
2026-07-17 8:56 ` Miklos Szeredi
2026-07-17 10:17 ` Horst Birthelmer
2026-07-17 20:33 ` Joanne Koong
2026-07-17 18:37 ` Haofeng Li
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox