arm64: dtsi: rk3399: optimize ipa parameters
[firefly-linux-kernel-4.4.55.git] / fs / pipe.c
index 3e7ab278bb0c0a3797e5112f30638c05851826fa..ab8dad3ccb6a8bac13a2eab25eeb2b68791d60f5 100644 (file)
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -21,7 +21,6 @@
 #include <linux/audit.h>
 #include <linux/syscalls.h>
 #include <linux/fcntl.h>
-#include <linux/aio.h>
 
 #include <asm/uaccess.h>
 #include <asm/ioctls.h>
@@ -39,6 +38,12 @@ unsigned int pipe_max_size = 1048576;
  */
 unsigned int pipe_min_size = PAGE_SIZE;
 
+/* Maximum allocatable pages per user. Hard limit is unset by default, soft
+ * matches default values.
+ */
+unsigned long pipe_user_pages_hard;
+unsigned long pipe_user_pages_soft = PIPE_DEF_BUFFERS * INR_OPEN_CUR;
+
 /*
  * We use a start+len construction, which provides full use of the 
  * allocated memory.
@@ -116,103 +121,6 @@ void pipe_wait(struct pipe_inode_info *pipe)
        pipe_lock(pipe);
 }
 
-static int
-pipe_iov_copy_from_user(void *addr, int *offset, struct iovec *iov,
-                       size_t *remaining, int atomic)
-{
-       unsigned long copy;
-
-       while (*remaining > 0) {
-               while (!iov->iov_len)
-                       iov++;
-               copy = min_t(unsigned long, *remaining, iov->iov_len);
-
-               if (atomic) {
-                       if (__copy_from_user_inatomic(addr + *offset,
-                                                     iov->iov_base, copy))
-                               return -EFAULT;
-               } else {
-                       if (copy_from_user(addr + *offset,
-                                          iov->iov_base, copy))
-                               return -EFAULT;
-               }
-               *offset += copy;
-               *remaining -= copy;
-               iov->iov_base += copy;
-               iov->iov_len -= copy;
-       }
-       return 0;
-}
-
-static int
-pipe_iov_copy_to_user(struct iovec *iov, void *addr, int *offset,
-                     size_t *remaining, int atomic)
-{
-       unsigned long copy;
-
-       while (*remaining > 0) {
-               while (!iov->iov_len)
-                       iov++;
-               copy = min_t(unsigned long, *remaining, iov->iov_len);
-
-               if (atomic) {
-                       if (__copy_to_user_inatomic(iov->iov_base,
-                                                   addr + *offset, copy))
-                               return -EFAULT;
-               } else {
-                       if (copy_to_user(iov->iov_base,
-                                        addr + *offset, copy))
-                               return -EFAULT;
-               }
-               *offset += copy;
-               *remaining -= 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)
 {
@@ -229,52 +137,6 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe,
                page_cache_release(page);
 }
 
-/**
- * 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
@@ -355,8 +217,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,
@@ -365,8 +225,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,
@@ -374,17 +232,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;
@@ -398,9 +253,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, remaining;
-                       int error, atomic;
+                       size_t chars = buf->len;
+                       size_t written;
+                       int error;
 
                        if (chars > total_len)
                                chars = total_len;
@@ -412,26 +267,14 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov,
                                break;
                        }
 
-                       atomic = !iov_fault_in_pages_write(iov, chars);
-                       remaining = chars;
-redo:
-                       addr = ops->map(pipe, buf, atomic);
-                       error = pipe_iov_copy_to_user(iov, addr, &buf->offset,
-                                                     &remaining, 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;
+                       buf->offset += chars;
                        buf->len -= chars;
 
                        /* Was it a packet buffer? Clean up and exit */
@@ -498,24 +341,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) {
@@ -534,33 +372,18 @@ 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;
-                       size_t remaining = chars;
-
-                       error = ops->confirm(pipe, buf);
-                       if (error)
+                       ret = ops->confirm(pipe, buf);
+                       if (ret)
                                goto out;
 
-                       iov_fault_in_pages_read(iov, chars);
-redo1:
-                       addr = ops->map(pipe, buf, atomic);
-                       error = pipe_iov_copy_from_user(addr, &offset, iov,
-                                                       &remaining, 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)) {
+                               ret = -EFAULT;
                                goto out;
                        }
-                       buf->len += chars;
-                       total_len -= chars;
-                       ret = chars;
-                       if (!total_len)
+                       do_wakeup = 1;
+                       buf->len += ret;
+                       if (!iov_iter_count(from))
                                goto out;
                }
        }
@@ -579,10 +402,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 offset = 0;
-                       size_t remaining;
+                       int copied;
 
                        if (!page) {
                                page = alloc_page(GFP_HIGHUSER);
@@ -598,41 +418,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);
-                       remaining = chars;
-redo2:
-                       if (atomic)
-                               src = kmap_atomic(page);
-                       else
-                               src = kmap(page);
-
-                       error = pipe_iov_copy_from_user(src, &offset, iov,
-                                                       &remaining, 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;
@@ -641,8 +439,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)
@@ -672,10 +469,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;
 }
@@ -791,20 +589,49 @@ pipe_fasync(int fd, struct file *filp, int on)
        return retval;
 }
 
+static void account_pipe_buffers(struct pipe_inode_info *pipe,
+                                 unsigned long old, unsigned long new)
+{
+       atomic_long_add(new - old, &pipe->user->pipe_bufs);
+}
+
+static bool too_many_pipe_buffers_soft(struct user_struct *user)
+{
+       return pipe_user_pages_soft &&
+              atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_soft;
+}
+
+static bool too_many_pipe_buffers_hard(struct user_struct *user)
+{
+       return pipe_user_pages_hard &&
+              atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_hard;
+}
+
 struct pipe_inode_info *alloc_pipe_info(void)
 {
        struct pipe_inode_info *pipe;
 
        pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL);
        if (pipe) {
-               pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * PIPE_DEF_BUFFERS, GFP_KERNEL);
+               unsigned long pipe_bufs = PIPE_DEF_BUFFERS;
+               struct user_struct *user = get_current_user();
+
+               if (!too_many_pipe_buffers_hard(user)) {
+                       if (too_many_pipe_buffers_soft(user))
+                               pipe_bufs = 1;
+                       pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * pipe_bufs, GFP_KERNEL);
+               }
+
                if (pipe->bufs) {
                        init_waitqueue_head(&pipe->wait);
                        pipe->r_counter = pipe->w_counter = 1;
-                       pipe->buffers = PIPE_DEF_BUFFERS;
+                       pipe->buffers = pipe_bufs;
+                       pipe->user = user;
+                       account_pipe_buffers(pipe, 0, pipe_bufs);
                        mutex_init(&pipe->mutex);
                        return pipe;
                }
+               free_uid(user);
                kfree(pipe);
        }
 
@@ -815,6 +642,8 @@ void free_pipe_info(struct pipe_inode_info *pipe)
 {
        int i;
 
+       account_pipe_buffers(pipe, pipe->buffers, 0);
+       free_uid(pipe->user);
        for (i = 0; i < pipe->buffers; i++) {
                struct pipe_buffer *buf = pipe->bufs + i;
                if (buf->ops)
@@ -834,7 +663,7 @@ static struct vfsmount *pipe_mnt __read_mostly;
 static char *pipefs_dname(struct dentry *dentry, char *buffer, int buflen)
 {
        return dynamic_dname(dentry, buffer, buflen, "pipe:[%lu]",
-                               dentry->d_inode->i_ino);
+                               d_inode(dentry)->i_ino);
 }
 
 static const struct dentry_operations pipefs_dentry_operations = {
@@ -900,17 +729,20 @@ int create_pipe_files(struct file **res, int flags)
 
        d_instantiate(path.dentry, inode);
 
-       err = -ENFILE;
        f = alloc_file(&path, FMODE_WRITE, &pipefifo_fops);
-       if (IS_ERR(f))
+       if (IS_ERR(f)) {
+               err = PTR_ERR(f);
                goto err_dentry;
+       }
 
        f->f_flags = O_WRONLY | (flags & (O_NONBLOCK | O_DIRECT));
        f->private_data = inode->i_pipe;
 
        res[0] = alloc_file(&path, FMODE_READ, &pipefifo_fops);
-       if (IS_ERR(res[0]))
+       if (IS_ERR(res[0])) {
+               err = PTR_ERR(res[0]);
                goto err_file;
+       }
 
        path_get(&path);
        res[0]->private_data = inode->i_pipe;
@@ -1153,10 +985,8 @@ err:
 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_iter      = pipe_read,
+       .write_iter     = pipe_write,
        .poll           = pipe_poll,
        .unlocked_ioctl = pipe_ioctl,
        .release        = pipe_release,
@@ -1205,6 +1035,7 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages)
                        memcpy(bufs + head, pipe->bufs, tail * sizeof(struct pipe_buffer));
        }
 
+       account_pipe_buffers(pipe, pipe->buffers, nr_pages);
        pipe->curbuf = 0;
        kfree(pipe->bufs);
        pipe->bufs = bufs;
@@ -1276,6 +1107,11 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
                if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) {
                        ret = -EPERM;
                        goto out;
+               } else if ((too_many_pipe_buffers_hard(pipe->user) ||
+                           too_many_pipe_buffers_soft(pipe->user)) &&
+                          !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) {
+                       ret = -EPERM;
+                       goto out;
                }
                ret = pipe_set_size(pipe, nr_pages);
                break;