diff options
Diffstat (limited to 'fs/pipe.c')
| -rw-r--r-- | fs/pipe.c | 296 | 
1 files changed, 49 insertions, 247 deletions
diff --git a/fs/pipe.c b/fs/pipe.c index d2c45e14e6d..21981e58e2a 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -116,99 +116,6 @@ void pipe_wait(struct pipe_inode_info *pipe)  	pipe_lock(pipe);  } -static int -pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len, -			int atomic) -{ -	unsigned long copy; - -	while (len > 0) { -		while (!iov->iov_len) -			iov++; -		copy = min_t(unsigned long, len, iov->iov_len); - -		if (atomic) { -			if (__copy_from_user_inatomic(to, iov->iov_base, copy)) -				return -EFAULT; -		} else { -			if (copy_from_user(to, iov->iov_base, copy)) -				return -EFAULT; -		} -		to += copy; -		len -= copy; -		iov->iov_base += copy; -		iov->iov_len -= copy; -	} -	return 0; -} - -static int -pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len, -		      int atomic) -{ -	unsigned long copy; - -	while (len > 0) { -		while (!iov->iov_len) -			iov++; -		copy = min_t(unsigned long, len, iov->iov_len); - -		if (atomic) { -			if (__copy_to_user_inatomic(iov->iov_base, from, copy)) -				return -EFAULT; -		} else { -			if (copy_to_user(iov->iov_base, from, copy)) -				return -EFAULT; -		} -		from += copy; -		len -= copy; -		iov->iov_base += copy; -		iov->iov_len -= copy; -	} -	return 0; -} - -/* - * Attempt to pre-fault in the user memory, so we can use atomic copies. - * Returns the number of bytes not faulted in. - */ -static int iov_fault_in_pages_write(struct iovec *iov, unsigned long len) -{ -	while (!iov->iov_len) -		iov++; - -	while (len > 0) { -		unsigned long this_len; - -		this_len = min_t(unsigned long, len, iov->iov_len); -		if (fault_in_pages_writeable(iov->iov_base, this_len)) -			break; - -		len -= this_len; -		iov++; -	} - -	return len; -} - -/* - * Pre-fault in the user memory, so we can use atomic copies. - */ -static void iov_fault_in_pages_read(struct iovec *iov, unsigned long len) -{ -	while (!iov->iov_len) -		iov++; - -	while (len > 0) { -		unsigned long this_len; - -		this_len = min_t(unsigned long, len, iov->iov_len); -		fault_in_pages_readable(iov->iov_base, this_len); -		len -= this_len; -		iov++; -	} -} -  static void anon_pipe_buf_release(struct pipe_inode_info *pipe,  				  struct pipe_buffer *buf)  { @@ -226,52 +133,6 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe,  }  /** - * generic_pipe_buf_map - virtually map a pipe buffer - * @pipe:	the pipe that the buffer belongs to - * @buf:	the buffer that should be mapped - * @atomic:	whether to use an atomic map - * - * Description: - *	This function returns a kernel virtual address mapping for the - *	pipe_buffer passed in @buf. If @atomic is set, an atomic map is provided - *	and the caller has to be careful not to fault before calling - *	the unmap function. - * - *	Note that this function calls kmap_atomic() if @atomic != 0. - */ -void *generic_pipe_buf_map(struct pipe_inode_info *pipe, -			   struct pipe_buffer *buf, int atomic) -{ -	if (atomic) { -		buf->flags |= PIPE_BUF_FLAG_ATOMIC; -		return kmap_atomic(buf->page); -	} - -	return kmap(buf->page); -} -EXPORT_SYMBOL(generic_pipe_buf_map); - -/** - * generic_pipe_buf_unmap - unmap a previously mapped pipe buffer - * @pipe:	the pipe that the buffer belongs to - * @buf:	the buffer that should be unmapped - * @map_data:	the data that the mapping function returned - * - * Description: - *	This function undoes the mapping that ->map() provided. - */ -void generic_pipe_buf_unmap(struct pipe_inode_info *pipe, -			    struct pipe_buffer *buf, void *map_data) -{ -	if (buf->flags & PIPE_BUF_FLAG_ATOMIC) { -		buf->flags &= ~PIPE_BUF_FLAG_ATOMIC; -		kunmap_atomic(map_data); -	} else -		kunmap(buf->page); -} -EXPORT_SYMBOL(generic_pipe_buf_unmap); - -/**   * generic_pipe_buf_steal - attempt to take ownership of a &pipe_buffer   * @pipe:	the pipe that the buffer belongs to   * @buf:	the buffer to attempt to steal @@ -351,8 +212,6 @@ EXPORT_SYMBOL(generic_pipe_buf_release);  static const struct pipe_buf_operations anon_pipe_buf_ops = {  	.can_merge = 1, -	.map = generic_pipe_buf_map, -	.unmap = generic_pipe_buf_unmap,  	.confirm = generic_pipe_buf_confirm,  	.release = anon_pipe_buf_release,  	.steal = generic_pipe_buf_steal, @@ -361,8 +220,6 @@ static const struct pipe_buf_operations anon_pipe_buf_ops = {  static const struct pipe_buf_operations packet_pipe_buf_ops = {  	.can_merge = 0, -	.map = generic_pipe_buf_map, -	.unmap = generic_pipe_buf_unmap,  	.confirm = generic_pipe_buf_confirm,  	.release = anon_pipe_buf_release,  	.steal = generic_pipe_buf_steal, @@ -370,17 +227,14 @@ static const struct pipe_buf_operations packet_pipe_buf_ops = {  };  static ssize_t -pipe_read(struct kiocb *iocb, const struct iovec *_iov, -	   unsigned long nr_segs, loff_t pos) +pipe_read(struct kiocb *iocb, struct iov_iter *to)  { +	size_t total_len = iov_iter_count(to);  	struct file *filp = iocb->ki_filp;  	struct pipe_inode_info *pipe = filp->private_data;  	int do_wakeup;  	ssize_t ret; -	struct iovec *iov = (struct iovec *)_iov; -	size_t total_len; -	total_len = iov_length(iov, nr_segs);  	/* Null read succeeds. */  	if (unlikely(total_len == 0))  		return 0; @@ -394,9 +248,9 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov,  			int curbuf = pipe->curbuf;  			struct pipe_buffer *buf = pipe->bufs + curbuf;  			const struct pipe_buf_operations *ops = buf->ops; -			void *addr;  			size_t chars = buf->len; -			int error, atomic; +			size_t written; +			int error;  			if (chars > total_len)  				chars = total_len; @@ -408,21 +262,10 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov,  				break;  			} -			atomic = !iov_fault_in_pages_write(iov, chars); -redo: -			addr = ops->map(pipe, buf, atomic); -			error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars, atomic); -			ops->unmap(pipe, buf, addr); -			if (unlikely(error)) { -				/* -				 * Just retry with the slow path if we failed. -				 */ -				if (atomic) { -					atomic = 0; -					goto redo; -				} +			written = copy_page_to_iter(buf->page, buf->offset, chars, to); +			if (unlikely(written < chars)) {  				if (!ret) -					ret = error; +					ret = -EFAULT;  				break;  			}  			ret += chars; @@ -493,24 +336,19 @@ static inline int is_packetized(struct file *file)  }  static ssize_t -pipe_write(struct kiocb *iocb, const struct iovec *_iov, -	    unsigned long nr_segs, loff_t ppos) +pipe_write(struct kiocb *iocb, struct iov_iter *from)  {  	struct file *filp = iocb->ki_filp;  	struct pipe_inode_info *pipe = filp->private_data; -	ssize_t ret; -	int do_wakeup; -	struct iovec *iov = (struct iovec *)_iov; -	size_t total_len; +	ssize_t ret = 0; +	int do_wakeup = 0; +	size_t total_len = iov_iter_count(from);  	ssize_t chars; -	total_len = iov_length(iov, nr_segs);  	/* Null write succeeds. */  	if (unlikely(total_len == 0))  		return 0; -	do_wakeup = 0; -	ret = 0;  	__pipe_lock(pipe);  	if (!pipe->readers) { @@ -529,32 +367,19 @@ pipe_write(struct kiocb *iocb, const struct iovec *_iov,  		int offset = buf->offset + buf->len;  		if (ops->can_merge && offset + chars <= PAGE_SIZE) { -			int error, atomic = 1; -			void *addr; - -			error = ops->confirm(pipe, buf); +			int error = ops->confirm(pipe, buf);  			if (error)  				goto out; -			iov_fault_in_pages_read(iov, chars); -redo1: -			addr = ops->map(pipe, buf, atomic); -			error = pipe_iov_copy_from_user(offset + addr, iov, -							chars, atomic); -			ops->unmap(pipe, buf, addr); -			ret = error; -			do_wakeup = 1; -			if (error) { -				if (atomic) { -					atomic = 0; -					goto redo1; -				} +			ret = copy_page_from_iter(buf->page, offset, chars, from); +			if (unlikely(ret < chars)) { +				error = -EFAULT;  				goto out;  			} +			do_wakeup = 1;  			buf->len += chars; -			total_len -= chars;  			ret = chars; -			if (!total_len) +			if (!iov_iter_count(from))  				goto out;  		}  	} @@ -573,8 +398,7 @@ redo1:  			int newbuf = (pipe->curbuf + bufs) & (pipe->buffers-1);  			struct pipe_buffer *buf = pipe->bufs + newbuf;  			struct page *page = pipe->tmp_page; -			char *src; -			int error, atomic = 1; +			int copied;  			if (!page) {  				page = alloc_page(GFP_HIGHUSER); @@ -590,40 +414,19 @@ redo1:  			 * FIXME! Is this really true?  			 */  			do_wakeup = 1; -			chars = PAGE_SIZE; -			if (chars > total_len) -				chars = total_len; - -			iov_fault_in_pages_read(iov, chars); -redo2: -			if (atomic) -				src = kmap_atomic(page); -			else -				src = kmap(page); - -			error = pipe_iov_copy_from_user(src, iov, chars, -							atomic); -			if (atomic) -				kunmap_atomic(src); -			else -				kunmap(page); - -			if (unlikely(error)) { -				if (atomic) { -					atomic = 0; -					goto redo2; -				} +			copied = copy_page_from_iter(page, 0, PAGE_SIZE, from); +			if (unlikely(copied < PAGE_SIZE && iov_iter_count(from))) {  				if (!ret) -					ret = error; +					ret = -EFAULT;  				break;  			} -			ret += chars; +			ret += copied;  			/* Insert it into the buffer array */  			buf->page = page;  			buf->ops = &anon_pipe_buf_ops;  			buf->offset = 0; -			buf->len = chars; +			buf->len = copied;  			buf->flags = 0;  			if (is_packetized(filp)) {  				buf->ops = &packet_pipe_buf_ops; @@ -632,8 +435,7 @@ redo2:  			pipe->nrbufs = ++bufs;  			pipe->tmp_page = NULL; -			total_len -= chars; -			if (!total_len) +			if (!iov_iter_count(from))  				break;  		}  		if (bufs < pipe->buffers) @@ -663,10 +465,11 @@ out:  		wake_up_interruptible_sync_poll(&pipe->wait, POLLIN | POLLRDNORM);  		kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);  	} -	if (ret > 0) { +	if (ret > 0 && sb_start_write_trylock(file_inode(filp)->i_sb)) {  		int err = file_update_time(filp);  		if (err)  			ret = err; +		sb_end_write(file_inode(filp)->i_sb);  	}  	return ret;  } @@ -726,11 +529,25 @@ pipe_poll(struct file *filp, poll_table *wait)  	return mask;  } +static void put_pipe_info(struct inode *inode, struct pipe_inode_info *pipe) +{ +	int kill = 0; + +	spin_lock(&inode->i_lock); +	if (!--pipe->files) { +		inode->i_pipe = NULL; +		kill = 1; +	} +	spin_unlock(&inode->i_lock); + +	if (kill) +		free_pipe_info(pipe); +} +  static int  pipe_release(struct inode *inode, struct file *file)  { -	struct pipe_inode_info *pipe = inode->i_pipe; -	int kill = 0; +	struct pipe_inode_info *pipe = file->private_data;  	__pipe_lock(pipe);  	if (file->f_mode & FMODE_READ) @@ -743,17 +560,9 @@ pipe_release(struct inode *inode, struct file *file)  		kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);  		kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);  	} -	spin_lock(&inode->i_lock); -	if (!--pipe->files) { -		inode->i_pipe = NULL; -		kill = 1; -	} -	spin_unlock(&inode->i_lock);  	__pipe_unlock(pipe); -	if (kill) -		free_pipe_info(pipe); - +	put_pipe_info(inode, pipe);  	return 0;  } @@ -1014,7 +823,6 @@ static int fifo_open(struct inode *inode, struct file *filp)  {  	struct pipe_inode_info *pipe;  	bool is_pipe = inode->i_sb->s_magic == PIPEFS_MAGIC; -	int kill = 0;  	int ret;  	filp->f_version = 0; @@ -1130,25 +938,19 @@ err_wr:  	goto err;  err: -	spin_lock(&inode->i_lock); -	if (!--pipe->files) { -		inode->i_pipe = NULL; -		kill = 1; -	} -	spin_unlock(&inode->i_lock);  	__pipe_unlock(pipe); -	if (kill) -		free_pipe_info(pipe); + +	put_pipe_info(inode, pipe);  	return ret;  }  const struct file_operations pipefifo_fops = {  	.open		= fifo_open,  	.llseek		= no_llseek, -	.read		= do_sync_read, -	.aio_read	= pipe_read, -	.write		= do_sync_write, -	.aio_write	= pipe_write, +	.read		= new_sync_read, +	.read_iter	= pipe_read, +	.write		= new_sync_write, +	.write_iter	= pipe_write,  	.poll		= pipe_poll,  	.unlocked_ioctl	= pipe_ioctl,  	.release	= pipe_release,  | 
