[PATCH v2 5/7] netfs: Fix read-gaps to remove netfs_folio from filled folio

David Howells
2026-04-14T08:20:42Z

Fix netfs_read_gaps() to remove the netfs_folio record from the folio
record before marking the folio uptodate if it successfully fills the gaps
around the dirty data in a streaming write folio (dirty, but not uptodate).

Found with:

fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \
/xfstest.test/junk --replay-ops=junk.fsxops

using the following as junk.fsxops:

truncate 0x0 0x138b1 0x8b15d *
write 0x507ee 0x10df7 0x927c0
write 0x19993 0x10e04 0x927c0 *
mapwrite 0x66214 0x1a253 0x927c0
copy_range 0xb704 0x89b9 0x24429 0x79380
write 0x2402b 0x144a2 0x90660 *
mapwrite 0x204d5 0x140a0 0x927c0 *
copy_range 0x1f72c 0x137d0 0x7a906 0x927c0 *
read 0 0x9157c 0x9157c

on cifs with the default cache option.

It shows folio 0x24 misbehaving if the FMODE_READ check is commented out in
netfs_perform_write():

if (//(file->f_mode & FMODE_READ) ||
netfs_is_cache_enabled(ctx)) {

and no fscache. This was initially found with the generic/522 xfstest.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Paulo Alcantara <pc@manguebit.org>
cc: Matthew Wilcox <willy@infradead.org>
cc: netfs@lists.linux.dev
cc: linux-fsdevel@vger.kernel.org
---
fs/netfs/buffered_read.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c
index a8c0d86118c5..c23173a061e8 100644
--- a/fs/netfs/buffered_read.c
+++ b/fs/netfs/buffered_read.c
@@ -397,6 +397,7 @@ static int netfs_read_gaps(struct file *file, struct folio *folio)
{
struct netfs_io_request *rreq;
struct address_space *mapping = folio->mapping;
+ struct netfs_group *group = netfs_folio_group(folio);
struct netfs_folio *finfo = netfs_folio_info(folio);
struct netfs_inode *ctx = netfs_inode(mapping->host);
struct folio *sink = NULL;
@@ -463,6 +464,10 @@ static int netfs_read_gaps(struct file *file, struct folio *folio)

ret = netfs_wait_for_read(rreq);
if (ret >= 0) {
+ folio_detach_private(folio);
+ netfs_put_group(group);
+ kfree(finfo);
+ trace_netfs_folio(folio, netfs_folio_trace_filled_gaps);
flush_dcache_folio(folio);
folio_mark_uptodate(folio);
}
@@ -498,10 +503,8 @@ int netfs_read_folio(struct file *file, struct folio *folio)
struct netfs_inode *ctx = netfs_inode(mapping->host);
int ret;

- if (folio_test_dirty(folio)) {
- trace_netfs_folio(folio, netfs_folio_trace_read_gaps);
+ if (folio_test_dirty(folio))
return netfs_read_gaps(file, folio);
- }

_enter("%lx", folio->index);