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 Signed-off-by: Kiryl Shutsemau (Meta) --- 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