Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ba5bb147 authored by Al Viro's avatar Al Viro
Browse files

pipe: take allocation and freeing of pipe_inode_info out of ->i_mutex



* new field - pipe->files; number of struct file over that pipe (all
  sharing the same inode, of course); protected by inode->i_lock.
* pipe_release() decrements pipe->files, clears inode->i_pipe when
  if the counter has reached 0 (all under ->i_lock) and, in that case,
  frees pipe after having done pipe_unlock()
* fifo_open() starts with grabbing ->i_lock, and either bumps pipe->files
  if ->i_pipe was non-NULL or allocates a new pipe (dropping and regaining
  ->i_lock) and rechecks ->i_pipe; if it's still NULL, inserts new pipe
  there, otherwise bumps ->i_pipe->files and frees the one we'd allocated.
  At that point we know that ->i_pipe is non-NULL and won't go away, so
  we can do pipe_lock() on it and proceed as we used to.  If we end up
  failing, decrement pipe->files and if it reaches 0 clear ->i_pipe and
  free the sucker after pipe_unlock().

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 18c03cfd
Loading
Loading
Loading
Loading
+51 −21
Original line number Diff line number Diff line
@@ -718,23 +718,30 @@ pipe_poll(struct file *filp, poll_table *wait)
static int
pipe_release(struct inode *inode, struct file *file)
{
	struct pipe_inode_info *pipe;
	struct pipe_inode_info *pipe = inode->i_pipe;
	int kill = 0;

	mutex_lock(&inode->i_mutex);
	pipe = inode->i_pipe;
	pipe_lock(pipe);
	if (file->f_mode & FMODE_READ)
		pipe->readers--;
	if (file->f_mode & FMODE_WRITE)
		pipe->writers--;

	if (!pipe->readers && !pipe->writers) {
		free_pipe_info(inode);
	} else {
	if (pipe->readers || pipe->writers) {
		wake_up_interruptible_sync_poll(&pipe->wait, POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM | POLLERR | POLLHUP);
		kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
		kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
	}
	mutex_unlock(&inode->i_mutex);
	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);

	return 0;
}
@@ -827,8 +834,9 @@ static struct inode * get_pipe_inode(void)
	pipe = alloc_pipe_info(inode);
	if (!pipe)
		goto fail_iput;
	inode->i_pipe = pipe;

	inode->i_pipe = pipe;
	pipe->files = 2;
	pipe->readers = pipe->writers = 1;
	inode->i_fop = &pipefifo_fops;

@@ -999,18 +1007,36 @@ 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;

	mutex_lock(&inode->i_mutex);
	filp->f_version = 0;

	spin_lock(&inode->i_lock);
	if (inode->i_pipe) {
		pipe = inode->i_pipe;
	if (!pipe) {
		ret = -ENOMEM;
		pipe->files++;
		spin_unlock(&inode->i_lock);
	} else {
		spin_unlock(&inode->i_lock);
		pipe = alloc_pipe_info(inode);
		if (!pipe)
			goto err_nocleanup;
			return -ENOMEM;
		pipe->files = 1;
		spin_lock(&inode->i_lock);
		if (unlikely(inode->i_pipe)) {
			inode->i_pipe->files++;
			spin_unlock(&inode->i_lock);
			__free_pipe_info(pipe);
			pipe = inode->i_pipe;
		} else {
			inode->i_pipe = pipe;
			spin_unlock(&inode->i_lock);
		}
	filp->f_version = 0;
	}
	/* OK, we have a pipe and it's pinned down */

	pipe_lock(pipe);

	/* We can only do regular read/write on fifos */
	filp->f_mode &= (FMODE_READ | FMODE_WRITE);
@@ -1080,7 +1106,7 @@ static int fifo_open(struct inode *inode, struct file *filp)
	}

	/* Ok! */
	mutex_unlock(&inode->i_mutex);
	pipe_unlock(pipe);
	return 0;

err_rd:
@@ -1096,11 +1122,15 @@ err_wr:
	goto err;

err:
	if (!pipe->readers && !pipe->writers)
		free_pipe_info(inode);

err_nocleanup:
	mutex_unlock(&inode->i_mutex);
	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);
	return ret;
}

+2 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ struct pipe_buffer {
 *	@tmp_page: cached released page
 *	@readers: number of current readers of this pipe
 *	@writers: number of current writers of this pipe
 *	@files: number of struct file refering this pipe (protected by ->i_lock)
 *	@waiting_writers: number of writers blocked waiting for room
 *	@r_counter: reader counter
 *	@w_counter: writer counter
@@ -47,6 +48,7 @@ struct pipe_inode_info {
	unsigned int nrbufs, curbuf, buffers;
	unsigned int readers;
	unsigned int writers;
	unsigned int files;
	unsigned int waiting_writers;
	unsigned int r_counter;
	unsigned int w_counter;