drm/nouveau/kms/nv50-: use NVIDIA's headers for core head_olut_set()
[linux-2.6-microblaze.git] / fs / pipe.c
index c7c4fb5..60dbee4 100644 (file)
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -24,6 +24,7 @@
 #include <linux/syscalls.h>
 #include <linux/fcntl.h>
 #include <linux/memcontrol.h>
+#include <linux/watch_queue.h>
 
 #include <linux/uaccess.h>
 #include <asm/ioctls.h>
@@ -259,14 +260,44 @@ pipe_read(struct kiocb *iocb, struct iov_iter *to)
                unsigned int tail = pipe->tail;
                unsigned int mask = pipe->ring_size - 1;
 
+#ifdef CONFIG_WATCH_QUEUE
+               if (pipe->note_loss) {
+                       struct watch_notification n;
+
+                       if (total_len < 8) {
+                               if (ret == 0)
+                                       ret = -ENOBUFS;
+                               break;
+                       }
+
+                       n.type = WATCH_TYPE_META;
+                       n.subtype = WATCH_META_LOSS_NOTIFICATION;
+                       n.info = watch_sizeof(n);
+                       if (copy_to_iter(&n, sizeof(n), to) != sizeof(n)) {
+                               if (ret == 0)
+                                       ret = -EFAULT;
+                               break;
+                       }
+                       ret += sizeof(n);
+                       total_len -= sizeof(n);
+                       pipe->note_loss = false;
+               }
+#endif
+
                if (!pipe_empty(head, tail)) {
                        struct pipe_buffer *buf = &pipe->bufs[tail & mask];
                        size_t chars = buf->len;
                        size_t written;
                        int error;
 
-                       if (chars > total_len)
+                       if (chars > total_len) {
+                               if (buf->flags & PIPE_BUF_FLAG_WHOLE) {
+                                       if (ret == 0)
+                                               ret = -ENOBUFS;
+                                       break;
+                               }
                                chars = total_len;
+                       }
 
                        error = pipe_buf_confirm(pipe, buf);
                        if (error) {
@@ -294,6 +325,10 @@ pipe_read(struct kiocb *iocb, struct iov_iter *to)
                        if (!buf->len) {
                                pipe_buf_release(pipe, buf);
                                spin_lock_irq(&pipe->rd_wait.lock);
+#ifdef CONFIG_WATCH_QUEUE
+                               if (buf->flags & PIPE_BUF_FLAG_LOSS)
+                                       pipe->note_loss = true;
+#endif
                                tail++;
                                pipe->tail = tail;
                                spin_unlock_irq(&pipe->rd_wait.lock);
@@ -405,6 +440,13 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
                goto out;
        }
 
+#ifdef CONFIG_WATCH_QUEUE
+       if (pipe->watch_queue) {
+               ret = -EXDEV;
+               goto out;
+       }
+#endif
+
        /*
         * Only wake up if the pipe started out empty, since
         * otherwise there should be no readers waiting.
@@ -574,22 +616,37 @@ static long pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        int count, head, tail, mask;
 
        switch (cmd) {
-               case FIONREAD:
-                       __pipe_lock(pipe);
-                       count = 0;
-                       head = pipe->head;
-                       tail = pipe->tail;
-                       mask = pipe->ring_size - 1;
+       case FIONREAD:
+               __pipe_lock(pipe);
+               count = 0;
+               head = pipe->head;
+               tail = pipe->tail;
+               mask = pipe->ring_size - 1;
 
-                       while (tail != head) {
-                               count += pipe->bufs[tail & mask].len;
-                               tail++;
-                       }
-                       __pipe_unlock(pipe);
+               while (tail != head) {
+                       count += pipe->bufs[tail & mask].len;
+                       tail++;
+               }
+               __pipe_unlock(pipe);
+
+               return put_user(count, (int __user *)arg);
 
-                       return put_user(count, (int __user *)arg);
-               default:
-                       return -ENOIOCTLCMD;
+#ifdef CONFIG_WATCH_QUEUE
+       case IOC_WATCH_QUEUE_SET_SIZE: {
+               int ret;
+               __pipe_lock(pipe);
+               ret = watch_queue_set_size(pipe, arg);
+               __pipe_unlock(pipe);
+               return ret;
+       }
+
+       case IOC_WATCH_QUEUE_SET_FILTER:
+               return watch_queue_set_filter(
+                       pipe, (struct watch_notification_filter __user *)arg);
+#endif
+
+       default:
+               return -ENOIOCTLCMD;
        }
 }
 
@@ -700,27 +757,27 @@ pipe_fasync(int fd, struct file *filp, int on)
        return retval;
 }
 
-static unsigned long account_pipe_buffers(struct user_struct *user,
-                                 unsigned long old, unsigned long new)
+unsigned long account_pipe_buffers(struct user_struct *user,
+                                  unsigned long old, unsigned long new)
 {
        return atomic_long_add_return(new - old, &user->pipe_bufs);
 }
 
-static bool too_many_pipe_buffers_soft(unsigned long user_bufs)
+bool too_many_pipe_buffers_soft(unsigned long user_bufs)
 {
        unsigned long soft_limit = READ_ONCE(pipe_user_pages_soft);
 
        return soft_limit && user_bufs > soft_limit;
 }
 
-static bool too_many_pipe_buffers_hard(unsigned long user_bufs)
+bool too_many_pipe_buffers_hard(unsigned long user_bufs)
 {
        unsigned long hard_limit = READ_ONCE(pipe_user_pages_hard);
 
        return hard_limit && user_bufs > hard_limit;
 }
 
-static bool is_unprivileged_user(void)
+bool pipe_is_unprivileged_user(void)
 {
        return !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN);
 }
@@ -742,12 +799,12 @@ struct pipe_inode_info *alloc_pipe_info(void)
 
        user_bufs = account_pipe_buffers(user, 0, pipe_bufs);
 
-       if (too_many_pipe_buffers_soft(user_bufs) && is_unprivileged_user()) {
+       if (too_many_pipe_buffers_soft(user_bufs) && pipe_is_unprivileged_user()) {
                user_bufs = account_pipe_buffers(user, pipe_bufs, 1);
                pipe_bufs = 1;
        }
 
-       if (too_many_pipe_buffers_hard(user_bufs) && is_unprivileged_user())
+       if (too_many_pipe_buffers_hard(user_bufs) && pipe_is_unprivileged_user())
                goto out_revert_acct;
 
        pipe->bufs = kcalloc(pipe_bufs, sizeof(struct pipe_buffer),
@@ -759,6 +816,7 @@ struct pipe_inode_info *alloc_pipe_info(void)
                pipe->r_counter = pipe->w_counter = 1;
                pipe->max_usage = pipe_bufs;
                pipe->ring_size = pipe_bufs;
+               pipe->nr_accounted = pipe_bufs;
                pipe->user = user;
                mutex_init(&pipe->mutex);
                return pipe;
@@ -776,7 +834,14 @@ void free_pipe_info(struct pipe_inode_info *pipe)
 {
        int i;
 
-       (void) account_pipe_buffers(pipe->user, pipe->ring_size, 0);
+#ifdef CONFIG_WATCH_QUEUE
+       if (pipe->watch_queue) {
+               watch_queue_clear(pipe->watch_queue);
+               put_watch_queue(pipe->watch_queue);
+       }
+#endif
+
+       (void) account_pipe_buffers(pipe->user, pipe->nr_accounted, 0);
        free_uid(pipe->user);
        for (i = 0; i < pipe->ring_size; i++) {
                struct pipe_buffer *buf = pipe->bufs + i;
@@ -852,6 +917,17 @@ int create_pipe_files(struct file **res, int flags)
        if (!inode)
                return -ENFILE;
 
+       if (flags & O_NOTIFICATION_PIPE) {
+#ifdef CONFIG_WATCH_QUEUE
+               if (watch_queue_init(inode->i_pipe) < 0) {
+                       iput(inode);
+                       return -ENOMEM;
+               }
+#else
+               return -ENOPKG;
+#endif
+       }
+
        f = alloc_file_pseudo(inode, pipe_mnt, "",
                                O_WRONLY | (flags & (O_NONBLOCK | O_DIRECT)),
                                &pipefifo_fops);
@@ -882,7 +958,7 @@ static int __do_pipe_flags(int *fd, struct file **files, int flags)
        int error;
        int fdw, fdr;
 
-       if (flags & ~(O_CLOEXEC | O_NONBLOCK | O_DIRECT))
+       if (flags & ~(O_CLOEXEC | O_NONBLOCK | O_DIRECT | O_NOTIFICATION_PIPE))
                return -EINVAL;
 
        error = create_pipe_files(files, flags);
@@ -1130,42 +1206,12 @@ unsigned int round_pipe_size(unsigned long size)
 }
 
 /*
- * Allocate a new array of pipe buffers and copy the info over. Returns the
- * pipe size if successful, or return -ERROR on error.
+ * Resize the pipe ring to a number of slots.
  */
-static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg)
+int pipe_resize_ring(struct pipe_inode_info *pipe, unsigned int nr_slots)
 {
        struct pipe_buffer *bufs;
-       unsigned int size, nr_slots, head, tail, mask, n;
-       unsigned long user_bufs;
-       long ret = 0;
-
-       size = round_pipe_size(arg);
-       nr_slots = size >> PAGE_SHIFT;
-
-       if (!nr_slots)
-               return -EINVAL;
-
-       /*
-        * If trying to increase the pipe capacity, check that an
-        * unprivileged user is not trying to exceed various limits
-        * (soft limit check here, hard limit check just below).
-        * Decreasing the pipe capacity is always permitted, even
-        * if the user is currently over a limit.
-        */
-       if (nr_slots > pipe->ring_size &&
-                       size > pipe_max_size && !capable(CAP_SYS_RESOURCE))
-               return -EPERM;
-
-       user_bufs = account_pipe_buffers(pipe->user, pipe->ring_size, nr_slots);
-
-       if (nr_slots > pipe->ring_size &&
-                       (too_many_pipe_buffers_hard(user_bufs) ||
-                        too_many_pipe_buffers_soft(user_bufs)) &&
-                       is_unprivileged_user()) {
-               ret = -EPERM;
-               goto out_revert_acct;
-       }
+       unsigned int head, tail, mask, n;
 
        /*
         * We can shrink the pipe, if arg is greater than the ring occupancy.
@@ -1177,17 +1223,13 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg)
        head = pipe->head;
        tail = pipe->tail;
        n = pipe_occupancy(pipe->head, pipe->tail);
-       if (nr_slots < n) {
-               ret = -EBUSY;
-               goto out_revert_acct;
-       }
+       if (nr_slots < n)
+               return -EBUSY;
 
        bufs = kcalloc(nr_slots, sizeof(*bufs),
                       GFP_KERNEL_ACCOUNT | __GFP_NOWARN);
-       if (unlikely(!bufs)) {
-               ret = -ENOMEM;
-               goto out_revert_acct;
-       }
+       if (unlikely(!bufs))
+               return -ENOMEM;
 
        /*
         * The pipe array wraps around, so just start the new one at zero
@@ -1215,16 +1257,68 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg)
        kfree(pipe->bufs);
        pipe->bufs = bufs;
        pipe->ring_size = nr_slots;
-       pipe->max_usage = nr_slots;
+       if (pipe->max_usage > nr_slots)
+               pipe->max_usage = nr_slots;
        pipe->tail = tail;
        pipe->head = head;
 
        /* This might have made more room for writers */
        wake_up_interruptible(&pipe->wr_wait);
+       return 0;
+}
+
+/*
+ * Allocate a new array of pipe buffers and copy the info over. Returns the
+ * pipe size if successful, or return -ERROR on error.
+ */
+static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg)
+{
+       unsigned long user_bufs;
+       unsigned int nr_slots, size;
+       long ret = 0;
+
+#ifdef CONFIG_WATCH_QUEUE
+       if (pipe->watch_queue)
+               return -EBUSY;
+#endif
+
+       size = round_pipe_size(arg);
+       nr_slots = size >> PAGE_SHIFT;
+
+       if (!nr_slots)
+               return -EINVAL;
+
+       /*
+        * If trying to increase the pipe capacity, check that an
+        * unprivileged user is not trying to exceed various limits
+        * (soft limit check here, hard limit check just below).
+        * Decreasing the pipe capacity is always permitted, even
+        * if the user is currently over a limit.
+        */
+       if (nr_slots > pipe->max_usage &&
+                       size > pipe_max_size && !capable(CAP_SYS_RESOURCE))
+               return -EPERM;
+
+       user_bufs = account_pipe_buffers(pipe->user, pipe->nr_accounted, nr_slots);
+
+       if (nr_slots > pipe->max_usage &&
+                       (too_many_pipe_buffers_hard(user_bufs) ||
+                        too_many_pipe_buffers_soft(user_bufs)) &&
+                       pipe_is_unprivileged_user()) {
+               ret = -EPERM;
+               goto out_revert_acct;
+       }
+
+       ret = pipe_resize_ring(pipe, nr_slots);
+       if (ret < 0)
+               goto out_revert_acct;
+
+       pipe->max_usage = nr_slots;
+       pipe->nr_accounted = nr_slots;
        return pipe->max_usage * PAGE_SIZE;
 
 out_revert_acct:
-       (void) account_pipe_buffers(pipe->user, nr_slots, pipe->ring_size);
+       (void) account_pipe_buffers(pipe->user, nr_slots, pipe->nr_accounted);
        return ret;
 }
 
@@ -1233,9 +1327,17 @@ out_revert_acct:
  * location, so checking ->i_pipe is not enough to verify that this is a
  * pipe.
  */
-struct pipe_inode_info *get_pipe_info(struct file *file)
+struct pipe_inode_info *get_pipe_info(struct file *file, bool for_splice)
 {
-       return file->f_op == &pipefifo_fops ? file->private_data : NULL;
+       struct pipe_inode_info *pipe = file->private_data;
+
+       if (file->f_op != &pipefifo_fops || !pipe)
+               return NULL;
+#ifdef CONFIG_WATCH_QUEUE
+       if (for_splice && pipe->watch_queue)
+               return NULL;
+#endif
+       return pipe;
 }
 
 long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -1243,7 +1345,7 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
        struct pipe_inode_info *pipe;
        long ret;
 
-       pipe = get_pipe_info(file);
+       pipe = get_pipe_info(file, false);
        if (!pipe)
                return -EBADF;