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 3/4] splice: remove pipe buffer stealing
Date: Tue, 10 Nov 2026 14:52:38 +0000 [thread overview]
Message-ID: <20261110145241.536019-4-kas@kernel.org> (raw)
In-Reply-To: <20261110145241.536019-1-kas@kernel.org>
Nothing calls pipe_buf_try_steal() any more. Remove ->try_steal() from
struct pipe_buf_operations, its implementations and the exported
generic_pipe_buf_try_steal().
PIPE_BUF_FLAG_LRU and PIPE_BUF_FLAG_GIFT were set only for stealing, and
go too. page_cache_pipe_buf_release() did nothing but drop the page and
the LRU flag, so page cache and user page buffers use
generic_pipe_buf_release().
vmsplice() still accepts SPLICE_F_GIFT, and splice() SPLICE_F_MOVE.
Both were hints, and nothing acts on them now.
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
fs/pipe.c | 43 ------------------
fs/splice.c | 96 +++------------------------------------
include/linux/pipe_fs_i.h | 36 ---------------
kernel/watch_queue.c | 4 --
mm/shmem.c | 7 ---
5 files changed, 6 insertions(+), 180 deletions(-)
diff --git a/fs/pipe.c b/fs/pipe.c
index 4fa69192e6fb..4c1122597a3c 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -222,48 +222,6 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe,
anon_pipe_put_page(pipe, page);
}
-static bool anon_pipe_buf_try_steal(struct pipe_inode_info *pipe,
- struct pipe_buffer *buf)
-{
- struct page *page = buf->page;
-
- if (page_count(page) != 1)
- return false;
- memcg_kmem_uncharge_page(page, 0);
- __SetPageLocked(page);
- return true;
-}
-
-/**
- * generic_pipe_buf_try_steal - attempt to take ownership of a &pipe_buffer
- * @pipe: the pipe that the buffer belongs to
- * @buf: the buffer to attempt to steal
- *
- * Description:
- * This function attempts to steal the &struct page attached to
- * @buf. If successful, this function returns 0 and returns with
- * the page locked. The caller may then reuse the page for whatever
- * he wishes; the typical use is insertion into a different file
- * page cache.
- */
-bool generic_pipe_buf_try_steal(struct pipe_inode_info *pipe,
- struct pipe_buffer *buf)
-{
- struct page *page = buf->page;
-
- /*
- * A reference of one is golden, that means that the owner of this
- * page is the only one holding a reference to it. lock the page
- * and return OK.
- */
- if (page_count(page) == 1) {
- lock_page(page);
- return true;
- }
- return false;
-}
-EXPORT_SYMBOL(generic_pipe_buf_try_steal);
-
/**
* generic_pipe_buf_get - get a reference to a &struct pipe_buffer
* @pipe: the pipe that the buffer belongs to
@@ -297,7 +255,6 @@ EXPORT_SYMBOL(generic_pipe_buf_release);
static const struct pipe_buf_operations anon_pipe_buf_ops = {
.release = anon_pipe_buf_release,
- .try_steal = anon_pipe_buf_try_steal,
.get = generic_pipe_buf_get,
};
diff --git a/fs/splice.c b/fs/splice.c
index e6d43329ed3f..ba8f6f9aa3f0 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -55,63 +55,6 @@ static noinline void pipe_clear_nowait(struct file *file)
} while (!try_cmpxchg(&file->f_mode, &fmode, fmode & ~FMODE_NOWAIT));
}
-/*
- * Attempt to steal a page from a pipe buffer. This should perhaps go into
- * a vm helper function, it's already simplified quite a bit by the
- * addition of remove_mapping(). If success is returned, the caller may
- * attempt to reuse this page for another destination.
- */
-static bool page_cache_pipe_buf_try_steal(struct pipe_inode_info *pipe,
- struct pipe_buffer *buf)
-{
- struct folio *folio = page_folio(buf->page);
- struct address_space *mapping;
-
- folio_lock(folio);
-
- mapping = folio_mapping(folio);
- if (mapping) {
- WARN_ON(!folio_test_uptodate(folio));
-
- /*
- * At least for ext2 with nobh option, we need to wait on
- * writeback completing on this folio, since we'll remove it
- * from the pagecache. Otherwise truncate wont wait on the
- * folio, allowing the disk blocks to be reused by someone else
- * before we actually wrote our data to them. fs corruption
- * ensues.
- */
- folio_wait_writeback(folio);
-
- if (!filemap_release_folio(folio, GFP_KERNEL))
- goto out_unlock;
-
- /*
- * If we succeeded in removing the mapping, set LRU flag
- * and return good.
- */
- if (remove_mapping(mapping, folio)) {
- buf->flags |= PIPE_BUF_FLAG_LRU;
- return true;
- }
- }
-
- /*
- * Raced with truncate or failed to remove folio from current
- * address space, unlock and return failure.
- */
-out_unlock:
- folio_unlock(folio);
- return false;
-}
-
-static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe,
- struct pipe_buffer *buf)
-{
- put_page(buf->page);
- buf->flags &= ~PIPE_BUF_FLAG_LRU;
-}
-
/*
* Check whether the contents of buf is OK to access. Since the content
* is a page cache page, IO may be in flight.
@@ -154,24 +97,12 @@ static int page_cache_pipe_buf_confirm(struct pipe_inode_info *pipe,
const struct pipe_buf_operations page_cache_pipe_buf_ops = {
.confirm = page_cache_pipe_buf_confirm,
- .release = page_cache_pipe_buf_release,
- .try_steal = page_cache_pipe_buf_try_steal,
+ .release = generic_pipe_buf_release,
.get = generic_pipe_buf_get,
};
-static bool user_page_pipe_buf_try_steal(struct pipe_inode_info *pipe,
- struct pipe_buffer *buf)
-{
- if (!(buf->flags & PIPE_BUF_FLAG_GIFT))
- return false;
-
- buf->flags |= PIPE_BUF_FLAG_LRU;
- return generic_pipe_buf_try_steal(pipe, buf);
-}
-
static const struct pipe_buf_operations user_page_pipe_buf_ops = {
- .release = page_cache_pipe_buf_release,
- .try_steal = user_page_pipe_buf_try_steal,
+ .release = generic_pipe_buf_release,
.get = generic_pipe_buf_get,
};
@@ -400,7 +331,6 @@ EXPORT_SYMBOL(copy_splice_read);
const struct pipe_buf_operations default_pipe_buf_ops = {
.release = generic_pipe_buf_release,
- .try_steal = generic_pipe_buf_try_steal,
.get = generic_pipe_buf_get,
};
@@ -1469,12 +1399,10 @@ static ssize_t __do_splice(struct file *in, loff_t __user *off_in,
}
static ssize_t iter_to_pipe(struct iov_iter *from,
- struct pipe_inode_info *pipe,
- unsigned int flags)
+ struct pipe_inode_info *pipe)
{
struct pipe_buffer buf = {
.ops = &user_page_pipe_buf_ops,
- .flags = flags
};
size_t total = 0;
ssize_t ret = 0;
@@ -1564,10 +1492,6 @@ static ssize_t vmsplice_to_pipe(struct file *file, struct iov_iter *iter,
{
struct pipe_inode_info *pipe;
ssize_t ret = 0;
- unsigned buf_flag = 0;
-
- if (flags & SPLICE_F_GIFT)
- buf_flag = PIPE_BUF_FLAG_GIFT;
pipe = get_pipe_info(file, true);
if (!pipe)
@@ -1578,7 +1502,7 @@ static ssize_t vmsplice_to_pipe(struct file *file, struct iov_iter *iter,
pipe_lock(pipe);
ret = wait_for_space(pipe, flags);
if (!ret)
- ret = iter_to_pipe(iter, pipe, buf_flag);
+ ret = iter_to_pipe(iter, pipe);
pipe_unlock(pipe);
if (ret > 0) {
wakeup_pipe_readers(pipe);
@@ -1839,11 +1763,7 @@ static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe,
}
*obuf = *ibuf;
- /*
- * Don't inherit the gift and merge flags, we need to
- * prevent multiple steals of this page.
- */
- obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
+ /* The page is shared with ipipe: don't append to it */
obuf->flags &= ~PIPE_BUF_FLAG_CAN_MERGE;
obuf->len = len;
@@ -1956,11 +1876,7 @@ static ssize_t link_pipe(struct pipe_inode_info *ipipe,
*obuf = *ibuf;
- /*
- * Don't inherit the gift and merge flag, we need to prevent
- * multiple steals of this page.
- */
- obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
+ /* The page is shared with ipipe: don't append to it */
obuf->flags &= ~PIPE_BUF_FLAG_CAN_MERGE;
if (obuf->len > len)
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h
index 6402930282e5..fb6570e80d01 100644
--- a/include/linux/pipe_fs_i.h
+++ b/include/linux/pipe_fs_i.h
@@ -4,9 +4,7 @@
#define PIPE_DEF_BUFFERS 16
-#define PIPE_BUF_FLAG_LRU 0x01 /* page is on the LRU */
#define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */
-#define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */
#define PIPE_BUF_FLAG_PACKET 0x08 /* read() as a packet */
#define PIPE_BUF_FLAG_CAN_MERGE 0x10 /* can merge buffers */
#define PIPE_BUF_FLAG_WHOLE 0x20 /* read() must return entire buffer or error */
@@ -127,16 +125,6 @@ struct pipe_inode_info {
#endif
};
-/*
- * Note on the nesting of these functions:
- *
- * ->confirm()
- * ->try_steal()
- *
- * That is, ->try_steal() must be called on a confirmed buffer. See below for
- * the meaning of each operation. Also see the kerneldoc in fs/pipe.c for the
- * pipe and generic variants of these hooks.
- */
struct pipe_buf_operations {
/*
* ->confirm() verifies that the data in the pipe buffer is there
@@ -153,16 +141,6 @@ struct pipe_buf_operations {
*/
void (*release)(struct pipe_inode_info *, struct pipe_buffer *);
- /*
- * Attempt to take ownership of the pipe buffer and its contents.
- * ->try_steal() returns %true for success, in which case the contents
- * of the pipe (the buf->page) is locked and now completely owned by the
- * caller. The page may then be transferred to a different mapping, the
- * most often used case is insertion into different file address space
- * cache.
- */
- bool (*try_steal)(struct pipe_inode_info *, struct pipe_buffer *);
-
/*
* Get a reference to the pipe buffer.
*/
@@ -304,19 +282,6 @@ static inline int pipe_buf_confirm(struct pipe_inode_info *pipe,
return buf->ops->confirm(pipe, buf);
}
-/**
- * pipe_buf_try_steal - attempt to take ownership of a pipe_buffer
- * @pipe: the pipe that the buffer belongs to
- * @buf: the buffer to attempt to steal
- */
-static inline bool pipe_buf_try_steal(struct pipe_inode_info *pipe,
- struct pipe_buffer *buf)
-{
- if (!buf->ops->try_steal)
- return false;
- return buf->ops->try_steal(pipe, buf);
-}
-
/* Differs from PIPE_BUF in that PIPE_SIZE is the length of the actual
memory allocation, whereas PIPE_BUF makes atomicity guarantees. */
#define PIPE_SIZE PAGE_SIZE
@@ -335,7 +300,6 @@ void free_pipe_info(struct pipe_inode_info *);
/* Generic pipe buffer ops functions */
bool generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *);
-bool generic_pipe_buf_try_steal(struct pipe_inode_info *, struct pipe_buffer *);
void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *);
extern const struct pipe_buf_operations nosteal_pipe_buf_ops;
diff --git a/kernel/watch_queue.c b/kernel/watch_queue.c
index 453dcf613205..e2bdd5fb0db1 100644
--- a/kernel/watch_queue.c
+++ b/kernel/watch_queue.c
@@ -77,13 +77,9 @@ static void watch_queue_pipe_buf_release(struct pipe_inode_info *pipe,
generic_pipe_buf_release(pipe, buf);
}
-// No try_steal function => no stealing
-#define watch_queue_pipe_buf_try_steal NULL
-
/* New data written to a pipe may be appended to a buffer with this type. */
static const struct pipe_buf_operations watch_queue_pipe_buf_ops = {
.release = watch_queue_pipe_buf_release,
- .try_steal = watch_queue_pipe_buf_try_steal,
.get = generic_pipe_buf_get,
};
diff --git a/mm/shmem.c b/mm/shmem.c
index af21a89abb0f..4a9440a1e1f8 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -3655,15 +3655,8 @@ static void zero_pipe_buf_release(struct pipe_inode_info *pipe,
{
}
-static bool zero_pipe_buf_try_steal(struct pipe_inode_info *pipe,
- struct pipe_buffer *buf)
-{
- return false;
-}
-
static const struct pipe_buf_operations zero_pipe_buf_ops = {
.release = zero_pipe_buf_release,
- .try_steal = zero_pipe_buf_try_steal,
.get = zero_pipe_buf_get,
};
--
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] " 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 ` [PATCH 2/4] fuse: don't move pipe buffers into the page cache Kiryl Shutsemau (Meta)
2026-11-10 16:25 ` Zi Yan
2026-11-10 14:52 ` Kiryl Shutsemau (Meta) [this message]
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-4-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®