From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
To: Christian Brauner <brauner@kernel.org>,
Alexander Viro <viro@zeniv.linux.org.uk>,
Miklos Szeredi <miklos@szeredi.hu>, Amit Shah <amit@kernel.org>
Cc: "Kiryl Shutsemau (Meta)" <kas@kernel.org>,
Jan Kara <jack@suse.cz>,
Linus Torvalds <torvalds@linux-foundation.org>,
"David Hildenbrand (Arm)" <david@kernel.org>,
Matthew Wilcox <willy@infradead.org>, Zi Yan <ziy@nvidia.com>,
Bernd Schubert <bschubert@ddn.com>,
Hugh Dickins <hughd@google.com>,
Baolin Wang <baolin.wang@linux.alibaba.com>,
Andrew Morton <akpm@linux-foundation.org>,
Arnd Bergmann <arnd@arndb.de>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Simon Horman <horms@kernel.org>,
linux-fsdevel@vger.kernel.org, fuse-devel@lists.linux.dev,
virtualization@lists.linux.dev, netdev@vger.kernel.org,
linux-mm@kvack.org, linux-kernel@vger.kernel.org,
kernel-team@meta.com
Subject: [PATCH 2/4] fuse: don't move pipe buffers into the page cache
Date: Tue, 10 Nov 2026 14:52:37 +0000 [thread overview]
Message-ID: <20261110145241.536019-3-kas@kernel.org> (raw)
In-Reply-To: <20261110145241.536019-1-kas@kernel.org>
A splice() into /dev/fuse with SPLICE_F_MOVE lets the reply to a read
request steal full pages from the pipe. Each stolen page replaces, in
the fuse file's page cache, the folio that the request allocated. The
page can be a page cache folio of another file, a page written into the
pipe, or a gifted user page.
Always copy. The server sees the same data, and the move was a hint
that splice() never promised to honour.
Keep advertising FUSE_SPLICE_MOVE. libfuse fails the mount of a
filesystem that asks for a capability the kernel does not offer.
Preparation for removing ->try_steal() from pipe buffers.
Suggested-by: David Hildenbrand (Arm) <david@kernel.org>
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
fs/fuse/args.h | 1 -
fs/fuse/dev.c | 144 +------------------------------------------
fs/fuse/file.c | 1 -
fs/fuse/fuse_dev_i.h | 1 -
4 files changed, 3 insertions(+), 144 deletions(-)
diff --git a/fs/fuse/args.h b/fs/fuse/args.h
index 5173264a1261..36ef0c6d5999 100644
--- a/fs/fuse/args.h
+++ b/fs/fuse/args.h
@@ -36,7 +36,6 @@ struct fuse_args {
bool user_pages:1;
bool out_argvar:1;
bool page_zeroing:1;
- bool page_replace:1;
bool may_block:1;
bool is_ext:1;
bool is_pinned:1;
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 3641abb90d6b..e7238eed21fb 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -1099,128 +1099,6 @@ static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size)
return ncpy;
}
-static int fuse_check_folio(struct folio *folio)
-{
- if (folio_mapped(folio) ||
- folio->mapping != NULL ||
- (folio->flags.f & PAGE_FLAGS_CHECK_AT_PREP &
- ~(1 << PG_locked |
- 1 << PG_referenced |
- 1 << PG_lru |
- 1 << PG_active |
- 1 << PG_workingset |
- 1 << PG_reclaim |
- 1 << PG_waiters |
- LRU_GEN_MASK | LRU_REFS_MASK))) {
- dump_page(&folio->page, "fuse: trying to steal weird page");
- return 1;
- }
- return 0;
-}
-
-/*
- * Attempt to steal a page from the splice() pipe and move it into the
- * pagecache. If successful, the pointer in @pagep will be updated. The
- * folio that was originally in @pagep will lose a reference and the new
- * folio returned in @pagep will carry a reference.
- */
-static int fuse_try_move_folio(struct fuse_copy_state *cs, struct folio **foliop)
-{
- int err;
- struct folio *oldfolio = *foliop;
- struct folio *newfolio;
- struct pipe_buffer *buf = cs->pipebufs;
-
- folio_get(oldfolio);
- err = unlock_request(cs->req);
- if (err)
- goto out_put_old;
-
- fuse_copy_finish(cs);
-
- err = pipe_buf_confirm(cs->pipe, buf);
- if (err)
- goto out_put_old;
-
- BUG_ON(!cs->nr_segs);
- cs->currbuf = buf;
- cs->len = buf->len;
- cs->pipebufs++;
- cs->nr_segs--;
-
- if (cs->len != folio_size(oldfolio))
- goto out_fallback;
-
- newfolio = page_folio(buf->page);
-
- if (folio_test_large(newfolio))
- goto out_fallback;
-
- /*
- * This is a new and locked page, it shouldn't be mapped or
- * have any special flags on it
- */
- if (WARN_ON(folio_mapped(oldfolio)))
- goto out_fallback;
- if (WARN_ON(folio_has_private(oldfolio)))
- goto out_fallback;
- if (WARN_ON(folio_test_dirty(oldfolio) ||
- folio_test_writeback(oldfolio)))
- goto out_fallback;
- if (WARN_ON(folio_test_mlocked(oldfolio)))
- goto out_fallback;
-
- if (!pipe_buf_try_steal(cs->pipe, buf))
- goto out_fallback;
-
- folio_clear_uptodate(newfolio);
- folio_clear_mappedtodisk(newfolio);
-
- if (fuse_check_folio(newfolio) != 0)
- goto out_fallback_unlock;
-
- err = lock_request(cs->req);
- if (err)
- goto out_fallback_unlock;
-
- replace_page_cache_folio(oldfolio, newfolio);
-
- folio_get(newfolio);
-
- if (!(buf->flags & PIPE_BUF_FLAG_LRU))
- folio_add_lru(newfolio);
-
- /*
- * Release while we have extra ref on stolen page. Otherwise
- * anon_pipe_buf_release() might think the page can be reused.
- */
- pipe_buf_release(cs->pipe, buf);
-
- *foliop = newfolio;
- folio_unlock(oldfolio);
- /* Drop ref for ap->pages[] array */
- folio_put(oldfolio);
- cs->len = 0;
-
- err = 0;
-out_put_old:
- /* Drop ref obtained in this function */
- folio_put(oldfolio);
- return err;
-
-out_fallback_unlock:
- folio_unlock(newfolio);
-out_fallback:
- cs->pg = buf->page;
- cs->offset = buf->offset;
-
- err = lock_request(cs->req);
- if (!err)
- err = 1;
-
- goto out_put_old;
-}
-
static int fuse_ref_folio(struct fuse_copy_state *cs, struct folio *folio,
unsigned offset, unsigned count)
{
@@ -1275,16 +1153,9 @@ int fuse_copy_folio(struct fuse_copy_state *cs, struct folio **foliop,
return fuse_ref_folio(cs, folio, offset, count);
}
} else if (!cs->len) {
- if (cs->move_folios && folio &&
- offset == 0 && count == folio_size(folio)) {
- err = fuse_try_move_folio(cs, foliop);
- if (err <= 0)
- return err;
- } else {
- err = fuse_copy_fill(cs);
- if (err)
- return err;
- }
+ err = fuse_copy_fill(cs);
+ if (err)
+ return err;
}
if (folio) {
void *mapaddr = kmap_local_folio(folio, offset);
@@ -1920,9 +1791,6 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
if (!smp_load_acquire(&fch->initialized) || !fch->connected)
goto copy_finish;
- /* Don't try to move folios (yet) */
- cs->move_folios = false;
-
err = fuse_notify(fch->conn, oh.error, nbytes - sizeof(oh), cs);
goto copy_finish;
}
@@ -1966,8 +1834,6 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
set_bit(FR_LOCKED, &req->flags);
spin_unlock(&fpq->lock);
cs->req = req;
- if (!req->args->page_replace)
- cs->move_folios = false;
if (oh.error)
err = nbytes != sizeof(oh) ? -EINVAL : 0;
@@ -2068,7 +1934,6 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
goto out_free;
*obuf = *ibuf;
- obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
obuf->len = rem;
ibuf->offset += obuf->len;
ibuf->len -= obuf->len;
@@ -2083,9 +1948,6 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
cs.nr_segs = nbuf;
cs.pipe = pipe;
- if (flags & SPLICE_F_MOVE)
- cs.move_folios = true;
-
ret = fuse_dev_do_write(fud, &cs, len);
pipe_lock(pipe);
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index d5e1cb53d86a..ab955e4c3e5d 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1096,7 +1096,6 @@ static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file,
ap->args.out_pages = true;
ap->args.page_zeroing = true;
- ap->args.page_replace = true;
/*
* Don't overflow end offset.
diff --git a/fs/fuse/fuse_dev_i.h b/fs/fuse/fuse_dev_i.h
index 4b412a76225f..a90ce26ded31 100644
--- a/fs/fuse/fuse_dev_i.h
+++ b/fs/fuse/fuse_dev_i.h
@@ -326,7 +326,6 @@ struct fuse_copy_state {
unsigned int len;
unsigned int offset;
bool write:1;
- bool move_folios:1;
bool is_uring:1;
/* set when the payload is zero-copied. folios are filled in place */
bool skip_folio_copy:1;
--
2.54.0
next prev parent reply other threads:[~2026-11-10 14:53 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 14:52 [PATCH 0/4] splice: remove pipe buffer stealing Kiryl Shutsemau (Meta)
2026-11-10 14:52 ` [PATCH 1/4] virtio_console: copy pipe buffers in splice_write() Kiryl Shutsemau (Meta)
2026-11-10 16:24 ` Zi Yan
2026-11-10 14:52 ` Kiryl Shutsemau (Meta) [this message]
2026-11-10 16:25 ` [PATCH 2/4] fuse: don't move pipe buffers into the page cache Zi Yan
2026-11-10 14:52 ` [PATCH 3/4] splice: remove pipe buffer stealing Kiryl Shutsemau (Meta)
2026-11-10 14:52 ` [PATCH 4/4] splice: replace nosteal_pipe_buf_ops with default_pipe_buf_ops Kiryl Shutsemau (Meta)
2026-11-10 15:59 ` [PATCH 0/4] splice: remove pipe buffer stealing Linus Torvalds
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261110145241.536019-3-kas@kernel.org \
--to=kas@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=amit@kernel.org \
--cc=arnd@arndb.de \
--cc=baolin.wang@linux.alibaba.com \
--cc=brauner@kernel.org \
--cc=bschubert@ddn.com \
--cc=davem@davemloft.net \
--cc=david@kernel.org \
--cc=edumazet@kernel.org \
--cc=fuse-devel@lists.linux.dev \
--cc=gregkh@linuxfoundation.org \
--cc=horms@kernel.org \
--cc=hughd@google.com \
--cc=jack@suse.cz \
--cc=kernel-team@meta.com \
--cc=kuba@kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=miklos@szeredi.hu \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=torvalds@linux-foundation.org \
--cc=viro@zeniv.linux.org.uk \
--cc=virtualization@lists.linux.dev \
--cc=willy@infradead.org \
--cc=ziy@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®