Linus TorvaldsLinux-2.6.12-rc21da177e21 years agoHistory
835 lines · 18.1 KB · C
RawDownload
/* *  linux/fs/pipe.c * *  Copyright (C) 1991, 1992, 1999  Linus Torvalds */ #include <linux/mm.h>#include <linux/file.h>#include <linux/poll.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/mount.h>#include <linux/pipe_fs_i.h>#include <linux/uio.h>#include <linux/highmem.h> #include <asm/uaccess.h>#include <asm/ioctls.h> /* * We use a start+len construction, which provides full use of the  * allocated memory. * -- Florian Coosmann (FGC) *  * Reads with count = 0 should always return 0. * -- Julian Bradfield 1999-06-07. * * FIFOs and Pipes now generate SIGIO for both readers and writers. * -- Jeremy Elson <jelson@circlemud.org> 2001-08-16 * * pipe_read & write cleanup * -- Manfred Spraul <manfred@colorfullife.com> 2002-05-09 */ /* Drop the inode semaphore and wait for a pipe event, atomically */void pipe_wait(struct inode * inode){	DEFINE_WAIT(wait); 	prepare_to_wait(PIPE_WAIT(*inode), &wait, TASK_INTERRUPTIBLE);	up(PIPE_SEM(*inode));	schedule();	finish_wait(PIPE_WAIT(*inode), &wait);	down(PIPE_SEM(*inode));} static inline intpipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len){	unsigned long copy; 	while (len > 0) {		while (!iov->iov_len)			iov++;		copy = min_t(unsigned long, len, iov->iov_len); 		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 inline intpipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len){	unsigned long copy; 	while (len > 0) {		while (!iov->iov_len)			iov++;		copy = min_t(unsigned long, len, iov->iov_len); 		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;} static void anon_pipe_buf_release(struct pipe_inode_info *info, struct pipe_buffer *buf){	struct page *page = buf->page; 	if (info->tmp_page) {		__free_page(page);		return;	}	info->tmp_page = page;} static void *anon_pipe_buf_map(struct file *file, struct pipe_inode_info *info, struct pipe_buffer *buf){	return kmap(buf->page);} static void anon_pipe_buf_unmap(struct pipe_inode_info *info, struct pipe_buffer *buf){	kunmap(buf->page);} static struct pipe_buf_operations anon_pipe_buf_ops = {	.can_merge = 1,	.map = anon_pipe_buf_map,	.unmap = anon_pipe_buf_unmap,	.release = anon_pipe_buf_release,}; static ssize_tpipe_readv(struct file *filp, const struct iovec *_iov,	   unsigned long nr_segs, loff_t *ppos){	struct inode *inode = filp->f_dentry->d_inode;	struct pipe_inode_info *info;	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; 	do_wakeup = 0;	ret = 0;	down(PIPE_SEM(*inode));	info = inode->i_pipe;	for (;;) {		int bufs = info->nrbufs;		if (bufs) {			int curbuf = info->curbuf;			struct pipe_buffer *buf = info->bufs + curbuf;			struct pipe_buf_operations *ops = buf->ops;			void *addr;			size_t chars = buf->len;			int error; 			if (chars > total_len)				chars = total_len; 			addr = ops->map(filp, info, buf);			error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars);			ops->unmap(info, buf);			if (unlikely(error)) {				if (!ret) ret = -EFAULT;				break;			}			ret += chars;			buf->offset += chars;			buf->len -= chars;			if (!buf->len) {				buf->ops = NULL;				ops->release(info, buf);				curbuf = (curbuf + 1) & (PIPE_BUFFERS-1);				info->curbuf = curbuf;				info->nrbufs = --bufs;				do_wakeup = 1;			}			total_len -= chars;			if (!total_len)				break;	/* common path: read succeeded */		}		if (bufs)	/* More to do? */			continue;		if (!PIPE_WRITERS(*inode))			break;		if (!PIPE_WAITING_WRITERS(*inode)) {			/* syscall merging: Usually we must not sleep			 * if O_NONBLOCK is set, or if we got some data.			 * But if a writer sleeps in kernel space, then			 * we can wait for that data without violating POSIX.			 */			if (ret)				break;			if (filp->f_flags & O_NONBLOCK) {				ret = -EAGAIN;				break;			}		}		if (signal_pending(current)) {			if (!ret) ret = -ERESTARTSYS;			break;		}		if (do_wakeup) {			wake_up_interruptible_sync(PIPE_WAIT(*inode)); 			kill_fasync(PIPE_FASYNC_WRITERS(*inode), SIGIO, POLL_OUT);		}		pipe_wait(inode);	}	up(PIPE_SEM(*inode));	/* Signal writers asynchronously that there is more room.  */	if (do_wakeup) {		wake_up_interruptible(PIPE_WAIT(*inode));		kill_fasync(PIPE_FASYNC_WRITERS(*inode), SIGIO, POLL_OUT);	}	if (ret > 0)		file_accessed(filp);	return ret;} static ssize_tpipe_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos){	struct iovec iov = { .iov_base = buf, .iov_len = count };	return pipe_readv(filp, &iov, 1, ppos);} static ssize_tpipe_writev(struct file *filp, const struct iovec *_iov,	    unsigned long nr_segs, loff_t *ppos){	struct inode *inode = filp->f_dentry->d_inode;	struct pipe_inode_info *info;	ssize_t ret;	int do_wakeup;	struct iovec *iov = (struct iovec *)_iov;	size_t total_len;	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;	down(PIPE_SEM(*inode));	info = inode->i_pipe; 	if (!PIPE_READERS(*inode)) {		send_sig(SIGPIPE, current, 0);		ret = -EPIPE;		goto out;	} 	/* We try to merge small writes */	chars = total_len & (PAGE_SIZE-1); /* size of the last buffer */	if (info->nrbufs && chars != 0) {		int lastbuf = (info->curbuf + info->nrbufs - 1) & (PIPE_BUFFERS-1);		struct pipe_buffer *buf = info->bufs + lastbuf;		struct pipe_buf_operations *ops = buf->ops;		int offset = buf->offset + buf->len;		if (ops->can_merge && offset + chars <= PAGE_SIZE) {			void *addr = ops->map(filp, info, buf);			int error = pipe_iov_copy_from_user(offset + addr, iov, chars);			ops->unmap(info, buf);			ret = error;			do_wakeup = 1;			if (error)				goto out;			buf->len += chars;			total_len -= chars;			ret = chars;			if (!total_len)				goto out;		}	} 	for (;;) {		int bufs;		if (!PIPE_READERS(*inode)) {			send_sig(SIGPIPE, current, 0);			if (!ret) ret = -EPIPE;			break;		}		bufs = info->nrbufs;		if (bufs < PIPE_BUFFERS) {			int newbuf = (info->curbuf + bufs) & (PIPE_BUFFERS-1);			struct pipe_buffer *buf = info->bufs + newbuf;			struct page *page = info->tmp_page;			int error; 			if (!page) {				page = alloc_page(GFP_HIGHUSER);				if (unlikely(!page)) {					ret = ret ? : -ENOMEM;					break;				}				info->tmp_page = page;			}			/* Always wakeup, even if the copy fails. Otherwise			 * we lock up (O_NONBLOCK-)readers that sleep due to			 * syscall merging.			 * FIXME! Is this really true?			 */			do_wakeup = 1;			chars = PAGE_SIZE;			if (chars > total_len)				chars = total_len; 			error = pipe_iov_copy_from_user(kmap(page), iov, chars);			kunmap(page);			if (unlikely(error)) {				if (!ret) ret = -EFAULT;				break;			}			ret += chars; 			/* Insert it into the buffer array */			buf->page = page;			buf->ops = &anon_pipe_buf_ops;			buf->offset = 0;			buf->len = chars;			info->nrbufs = ++bufs;			info->tmp_page = NULL; 			total_len -= chars;			if (!total_len)				break;		}		if (bufs < PIPE_BUFFERS)			continue;		if (filp->f_flags & O_NONBLOCK) {			if (!ret) ret = -EAGAIN;			break;		}		if (signal_pending(current)) {			if (!ret) ret = -ERESTARTSYS;			break;		}		if (do_wakeup) {			wake_up_interruptible_sync(PIPE_WAIT(*inode));			kill_fasync(PIPE_FASYNC_READERS(*inode), SIGIO, POLL_IN);			do_wakeup = 0;		}		PIPE_WAITING_WRITERS(*inode)++;		pipe_wait(inode);		PIPE_WAITING_WRITERS(*inode)--;	}out:	up(PIPE_SEM(*inode));	if (do_wakeup) {		wake_up_interruptible(PIPE_WAIT(*inode));		kill_fasync(PIPE_FASYNC_READERS(*inode), SIGIO, POLL_IN);	}	if (ret > 0)		inode_update_time(inode, 1);	/* mtime and ctime */	return ret;} static ssize_tpipe_write(struct file *filp, const char __user *buf,	   size_t count, loff_t *ppos){	struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = count };	return pipe_writev(filp, &iov, 1, ppos);} static ssize_tbad_pipe_r(struct file *filp, char __user *buf, size_t count, loff_t *ppos){	return -EBADF;} static ssize_tbad_pipe_w(struct file *filp, const char __user *buf, size_t count, loff_t *ppos){	return -EBADF;} static intpipe_ioctl(struct inode *pino, struct file *filp,	   unsigned int cmd, unsigned long arg){	struct inode *inode = filp->f_dentry->d_inode;	struct pipe_inode_info *info;	int count, buf, nrbufs; 	switch (cmd) {		case FIONREAD:			down(PIPE_SEM(*inode));			info =  inode->i_pipe;			count = 0;			buf = info->curbuf;			nrbufs = info->nrbufs;			while (--nrbufs >= 0) {				count += info->bufs[buf].len;				buf = (buf+1) & (PIPE_BUFFERS-1);			}			up(PIPE_SEM(*inode));			return put_user(count, (int __user *)arg);		default:			return -EINVAL;	}} /* No kernel lock held - fine */static unsigned intpipe_poll(struct file *filp, poll_table *wait){	unsigned int mask;	struct inode *inode = filp->f_dentry->d_inode;	struct pipe_inode_info *info = inode->i_pipe;	int nrbufs; 	poll_wait(filp, PIPE_WAIT(*inode), wait); 	/* Reading only -- no need for acquiring the semaphore.  */	nrbufs = info->nrbufs;	mask = 0;	if (filp->f_mode & FMODE_READ) {		mask = (nrbufs > 0) ? POLLIN | POLLRDNORM : 0;		if (!PIPE_WRITERS(*inode) && filp->f_version != PIPE_WCOUNTER(*inode))			mask |= POLLHUP;	} 	if (filp->f_mode & FMODE_WRITE) {		mask |= (nrbufs < PIPE_BUFFERS) ? POLLOUT | POLLWRNORM : 0;		if (!PIPE_READERS(*inode))			mask |= POLLERR;	} 	return mask;} /* FIXME: most Unices do not set POLLERR for fifos */#define fifo_poll pipe_poll static intpipe_release(struct inode *inode, int decr, int decw){	down(PIPE_SEM(*inode));	PIPE_READERS(*inode) -= decr;	PIPE_WRITERS(*inode) -= decw;	if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) {		free_pipe_info(inode);	} else {		wake_up_interruptible(PIPE_WAIT(*inode));		kill_fasync(PIPE_FASYNC_READERS(*inode), SIGIO, POLL_IN);		kill_fasync(PIPE_FASYNC_WRITERS(*inode), SIGIO, POLL_OUT);	}	up(PIPE_SEM(*inode)); 	return 0;} static intpipe_read_fasync(int fd, struct file *filp, int on){	struct inode *inode = filp->f_dentry->d_inode;	int retval; 	down(PIPE_SEM(*inode));	retval = fasync_helper(fd, filp, on, PIPE_FASYNC_READERS(*inode));	up(PIPE_SEM(*inode)); 	if (retval < 0)		return retval; 	return 0;}  static intpipe_write_fasync(int fd, struct file *filp, int on){	struct inode *inode = filp->f_dentry->d_inode;	int retval; 	down(PIPE_SEM(*inode));	retval = fasync_helper(fd, filp, on, PIPE_FASYNC_WRITERS(*inode));	up(PIPE_SEM(*inode)); 	if (retval < 0)		return retval; 	return 0;}  static intpipe_rdwr_fasync(int fd, struct file *filp, int on){	struct inode *inode = filp->f_dentry->d_inode;	int retval; 	down(PIPE_SEM(*inode)); 	retval = fasync_helper(fd, filp, on, PIPE_FASYNC_READERS(*inode)); 	if (retval >= 0)		retval = fasync_helper(fd, filp, on, PIPE_FASYNC_WRITERS(*inode)); 	up(PIPE_SEM(*inode)); 	if (retval < 0)		return retval; 	return 0;} 

Lines 1–500 of 835.

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