1790 lines · 40.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * "splice": joining two ropes together by interweaving their strands. * * This is the "extended pipe" functionality, where a pipe is used as * an arbitrary in-memory buffer. Think of a pipe as a small kernel * buffer that you can use to transfer data from one end to the other. * * The traditional unix read/write is extended with a "splice()" operation * that transfers data buffers to or from a pipe buffer. * * Named by Larry McVoy, original implementation from Linus, extended by * Jens to support splicing to files, network, direct splicing, etc and * fixing lots of bugs. * * Copyright (C) 2005-2006 Jens Axboe <axboe@kernel.dk> * Copyright (C) 2005-2006 Linus Torvalds <torvalds@osdl.org> * Copyright (C) 2006 Ingo Molnar <mingo@elte.hu> * */#include <linux/bvec.h>#include <linux/fs.h>#include <linux/file.h>#include <linux/pagemap.h>#include <linux/splice.h>#include <linux/memcontrol.h>#include <linux/mm_inline.h>#include <linux/swap.h>#include <linux/writeback.h>#include <linux/export.h>#include <linux/syscalls.h>#include <linux/uio.h>#include <linux/security.h>#include <linux/gfp.h>#include <linux/socket.h>#include <linux/compat.h>#include <linux/sched/signal.h> #include "internal.h" /* * 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 page *page = buf->page;	struct address_space *mapping; 	lock_page(page); 	mapping = page_mapping(page);	if (mapping) {		WARN_ON(!PageUptodate(page)); 		/*		 * At least for ext2 with nobh option, we need to wait on		 * writeback completing on this page, since we'll remove it		 * from the pagecache.  Otherwise truncate wont wait on the		 * page, allowing the disk blocks to be reused by someone else		 * before we actually wrote our data to them. fs corruption		 * ensues.		 */		wait_on_page_writeback(page); 		if (page_has_private(page) &&		    !try_to_release_page(page, GFP_KERNEL))			goto out_unlock; 		/*		 * If we succeeded in removing the mapping, set LRU flag		 * and return good.		 */		if (remove_mapping(mapping, page)) {			buf->flags |= PIPE_BUF_FLAG_LRU;			return true;		}	} 	/*	 * Raced with truncate or failed to remove page from current	 * address space, unlock and return failure.	 */out_unlock:	unlock_page(page);	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. */static int page_cache_pipe_buf_confirm(struct pipe_inode_info *pipe,				       struct pipe_buffer *buf){	struct page *page = buf->page;	int err; 	if (!PageUptodate(page)) {		lock_page(page); 		/*		 * Page got truncated/unhashed. This will cause a 0-byte		 * splice, if this is the first page.		 */		if (!page->mapping) {			err = -ENODATA;			goto error;		} 		/*		 * Uh oh, read-error from disk.		 */		if (!PageUptodate(page)) {			err = -EIO;			goto error;		} 		/*		 * Page is ok afterall, we are done.		 */		unlock_page(page);	} 	return 0;error:	unlock_page(page);	return err;} 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,	.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,	.get		= generic_pipe_buf_get,}; static void wakeup_pipe_readers(struct pipe_inode_info *pipe){	smp_mb();	if (waitqueue_active(&pipe->rd_wait))		wake_up_interruptible(&pipe->rd_wait);	kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);} /** * splice_to_pipe - fill passed data into a pipe * @pipe:	pipe to fill * @spd:	data to fill * * Description: *    @spd contains a map of pages and len/offset tuples, along with *    the struct pipe_buf_operations associated with these pages. This *    function will link that data to the pipe. * */ssize_t splice_to_pipe(struct pipe_inode_info *pipe,		       struct splice_pipe_desc *spd){	unsigned int spd_pages = spd->nr_pages;	unsigned int tail = pipe->tail;	unsigned int head = pipe->head;	unsigned int mask = pipe->ring_size - 1;	int ret = 0, page_nr = 0; 	if (!spd_pages)		return 0; 	if (unlikely(!pipe->readers)) {		send_sig(SIGPIPE, current, 0);		ret = -EPIPE;		goto out;	} 	while (!pipe_full(head, tail, pipe->max_usage)) {		struct pipe_buffer *buf = &pipe->bufs[head & mask]; 		buf->page = spd->pages[page_nr];		buf->offset = spd->partial[page_nr].offset;		buf->len = spd->partial[page_nr].len;		buf->private = spd->partial[page_nr].private;		buf->ops = spd->ops;		buf->flags = 0; 		head++;		pipe->head = head;		page_nr++;		ret += buf->len; 		if (!--spd->nr_pages)			break;	} 	if (!ret)		ret = -EAGAIN; out:	while (page_nr < spd_pages)		spd->spd_release(spd, page_nr++); 	return ret;}EXPORT_SYMBOL_GPL(splice_to_pipe); ssize_t add_to_pipe(struct pipe_inode_info *pipe, struct pipe_buffer *buf){	unsigned int head = pipe->head;	unsigned int tail = pipe->tail;	unsigned int mask = pipe->ring_size - 1;	int ret; 	if (unlikely(!pipe->readers)) {		send_sig(SIGPIPE, current, 0);		ret = -EPIPE;	} else if (pipe_full(head, tail, pipe->max_usage)) {		ret = -EAGAIN;	} else {		pipe->bufs[head & mask] = *buf;		pipe->head = head + 1;		return buf->len;	}	pipe_buf_release(pipe, buf);	return ret;}EXPORT_SYMBOL(add_to_pipe); /* * Check if we need to grow the arrays holding pages and partial page * descriptions. */int splice_grow_spd(const struct pipe_inode_info *pipe, struct splice_pipe_desc *spd){	unsigned int max_usage = READ_ONCE(pipe->max_usage); 	spd->nr_pages_max = max_usage;	if (max_usage <= PIPE_DEF_BUFFERS)		return 0; 	spd->pages = kmalloc_array(max_usage, sizeof(struct page *), GFP_KERNEL);	spd->partial = kmalloc_array(max_usage, sizeof(struct partial_page),				     GFP_KERNEL); 	if (spd->pages && spd->partial)		return 0; 	kfree(spd->pages);	kfree(spd->partial);	return -ENOMEM;} void splice_shrink_spd(struct splice_pipe_desc *spd){	if (spd->nr_pages_max <= PIPE_DEF_BUFFERS)		return; 	kfree(spd->pages);	kfree(spd->partial);} /** * generic_file_splice_read - splice data from file to a pipe * @in:		file to splice from * @ppos:	position in @in * @pipe:	pipe to splice to * @len:	number of bytes to splice * @flags:	splice modifier flags * * Description: *    Will read pages from given file and fill them into a pipe. Can be *    used as long as it has more or less sane ->read_iter(). * */ssize_t generic_file_splice_read(struct file *in, loff_t *ppos,				 struct pipe_inode_info *pipe, size_t len,				 unsigned int flags){	struct iov_iter to;	struct kiocb kiocb;	unsigned int i_head;	int ret; 	iov_iter_pipe(&to, READ, pipe, len);	i_head = to.head;	init_sync_kiocb(&kiocb, in);	kiocb.ki_pos = *ppos;	ret = call_read_iter(in, &kiocb, &to);	if (ret > 0) {		*ppos = kiocb.ki_pos;		file_accessed(in);	} else if (ret < 0) {		to.head = i_head;		to.iov_offset = 0;		iov_iter_advance(&to, 0); /* to free what was emitted */		/*		 * callers of ->splice_read() expect -EAGAIN on		 * "can't put anything in there", rather than -EFAULT.		 */		if (ret == -EFAULT)			ret = -EAGAIN;	} 	return ret;}EXPORT_SYMBOL(generic_file_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,}; /* Pipe buffer operations for a socket and similar. */const struct pipe_buf_operations nosteal_pipe_buf_ops = {	.release	= generic_pipe_buf_release,	.get		= generic_pipe_buf_get,};EXPORT_SYMBOL(nosteal_pipe_buf_ops); static ssize_t kernel_readv(struct file *file, const struct kvec *vec,			    unsigned long vlen, loff_t offset){	mm_segment_t old_fs;	loff_t pos = offset;	ssize_t res; 	old_fs = get_fs();	set_fs(KERNEL_DS);	/* The cast to a user pointer is valid due to the set_fs() */	res = vfs_readv(file, (const struct iovec __user *)vec, vlen, &pos, 0);	set_fs(old_fs); 	return res;} static ssize_t default_file_splice_read(struct file *in, loff_t *ppos,				 struct pipe_inode_info *pipe, size_t len,				 unsigned int flags){	struct kvec *vec, __vec[PIPE_DEF_BUFFERS];	struct iov_iter to;	struct page **pages;	unsigned int nr_pages;	unsigned int mask;	size_t offset, base, copied = 0;	ssize_t res;	int i; 	if (pipe_full(pipe->head, pipe->tail, pipe->max_usage))		return -EAGAIN; 	/*	 * Try to keep page boundaries matching to source pagecache ones -	 * it probably won't be much help, but...	 */	offset = *ppos & ~PAGE_MASK; 	iov_iter_pipe(&to, READ, pipe, len + offset); 	res = iov_iter_get_pages_alloc(&to, &pages, len + offset, &base);	if (res <= 0)		return -ENOMEM; 	nr_pages = DIV_ROUND_UP(res + base, PAGE_SIZE); 	vec = __vec;	if (nr_pages > PIPE_DEF_BUFFERS) {		vec = kmalloc_array(nr_pages, sizeof(struct kvec), GFP_KERNEL);		if (unlikely(!vec)) {			res = -ENOMEM;			goto out;		}	} 	mask = pipe->ring_size - 1;	pipe->bufs[to.head & mask].offset = offset;	pipe->bufs[to.head & mask].len -= offset; 	for (i = 0; i < nr_pages; i++) {		size_t this_len = min_t(size_t, len, PAGE_SIZE - offset);		vec[i].iov_base = page_address(pages[i]) + offset;		vec[i].iov_len = this_len;		len -= this_len;		offset = 0;	} 	res = kernel_readv(in, vec, nr_pages, *ppos);	if (res > 0) {		copied = res;		*ppos += res;	} 	if (vec != __vec)		kfree(vec);out:	for (i = 0; i < nr_pages; i++)		put_page(pages[i]);	kvfree(pages);	iov_iter_advance(&to, copied);	/* truncates and discards */	return res;} /* * Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos' * using sendpage(). Return the number of bytes sent. */static int pipe_to_sendpage(struct pipe_inode_info *pipe,			    struct pipe_buffer *buf, struct splice_desc *sd){	struct file *file = sd->u.file;	loff_t pos = sd->pos;	int more; 	if (!likely(file->f_op->sendpage))		return -EINVAL; 	more = (sd->flags & SPLICE_F_MORE) ? MSG_MORE : 0; 	if (sd->len < sd->total_len &&	    pipe_occupancy(pipe->head, pipe->tail) > 1)		more |= MSG_SENDPAGE_NOTLAST; 	return file->f_op->sendpage(file, buf->page, buf->offset,				    sd->len, &pos, more);} static void wakeup_pipe_writers(struct pipe_inode_info *pipe){	smp_mb();	if (waitqueue_active(&pipe->wr_wait))		wake_up_interruptible(&pipe->wr_wait);	kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);} /** * splice_from_pipe_feed - feed available data from a pipe to a file * @pipe:	pipe to splice from * @sd:		information to @actor * @actor:	handler that splices the data * * Description: *    This function loops over the pipe and calls @actor to do the *    actual moving of a single struct pipe_buffer to the desired *    destination.  It returns when there's no more buffers left in *    the pipe or if the requested number of bytes (@sd->total_len) *    have been copied.  It returns a positive number (one) if the *    pipe needs to be filled with more data, zero if the required *    number of bytes have been copied and -errno on error. * *    This, together with splice_from_pipe_{begin,end,next}, may be *    used to implement the functionality of __splice_from_pipe() when *    locking is required around copying the pipe buffers to the *    destination. */static int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_desc *sd,			  splice_actor *actor){	unsigned int head = pipe->head;	unsigned int tail = pipe->tail;	unsigned int mask = pipe->ring_size - 1;	int ret; 	while (!pipe_empty(head, tail)) {		struct pipe_buffer *buf = &pipe->bufs[tail & mask]; 		sd->len = buf->len;		if (sd->len > sd->total_len)			sd->len = sd->total_len; 		ret = pipe_buf_confirm(pipe, buf);		if (unlikely(ret)) {			if (ret == -ENODATA)				ret = 0;			return ret;		}

Lines 1–500 of 1790.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t