pipe: Rearrange sequence in pipe_write() to preallocate slot
[linux-2.6-microblaze.git] / fs / pipe.c
index c16950e..ce77ac0 100644 (file)
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -387,7 +387,7 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *filp = iocb->ki_filp;
        struct pipe_inode_info *pipe = filp->private_data;
-       unsigned int head, tail, max_usage, mask;
+       unsigned int head, max_usage, mask;
        ssize_t ret = 0;
        int do_wakeup = 0;
        size_t total_len = iov_iter_count(from);
@@ -405,14 +405,13 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
                goto out;
        }
 
-       tail = pipe->tail;
        head = pipe->head;
        max_usage = pipe->max_usage;
        mask = pipe->ring_size - 1;
 
        /* We try to merge small writes */
        chars = total_len & (PAGE_SIZE-1); /* size of the last buffer */
-       if (!pipe_empty(head, tail) && chars != 0) {
+       if (!pipe_empty(head, pipe->tail) && chars != 0) {
                struct pipe_buffer *buf = &pipe->bufs[(head - 1) & mask];
                int offset = buf->offset + buf->len;
 
@@ -441,8 +440,8 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
                        break;
                }
 
-               tail = pipe->tail;
-               if (!pipe_full(head, tail, max_usage)) {
+               head = pipe->head;
+               if (!pipe_full(head, pipe->tail, max_usage)) {
                        struct pipe_buffer *buf = &pipe->bufs[head & mask];
                        struct page *page = pipe->tmp_page;
                        int copied;
@@ -455,40 +454,56 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
                                }
                                pipe->tmp_page = page;
                        }
+
+                       /* Allocate a slot in the ring in advance and attach an
+                        * empty buffer.  If we fault or otherwise fail to use
+                        * it, either the reader will consume it or it'll still
+                        * be there for the next write.
+                        */
+                       spin_lock_irq(&pipe->wait.lock);
+
+                       head = pipe->head;
+                       pipe->head = head + 1;
+
                        /* Always wake up, 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;
-                       copied = copy_page_from_iter(page, 0, PAGE_SIZE, from);
-                       if (unlikely(copied < PAGE_SIZE && iov_iter_count(from))) {
-                               if (!ret)
-                                       ret = -EFAULT;
-                               break;
-                       }
-                       ret += copied;
+                       wake_up_interruptible_sync_poll_locked(
+                               &pipe->wait, EPOLLIN | EPOLLRDNORM);
+
+                       spin_unlock_irq(&pipe->wait.lock);
+                       kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
 
                        /* Insert it into the buffer array */
+                       buf = &pipe->bufs[head & mask];
                        buf->page = page;
                        buf->ops = &anon_pipe_buf_ops;
                        buf->offset = 0;
-                       buf->len = copied;
+                       buf->len = 0;
                        buf->flags = 0;
                        if (is_packetized(filp)) {
                                buf->ops = &packet_pipe_buf_ops;
                                buf->flags = PIPE_BUF_FLAG_PACKET;
                        }
-
-                       head++;
-                       pipe->head = head;
                        pipe->tmp_page = NULL;
 
+                       copied = copy_page_from_iter(page, 0, PAGE_SIZE, from);
+                       if (unlikely(copied < PAGE_SIZE && iov_iter_count(from))) {
+                               if (!ret)
+                                       ret = -EFAULT;
+                               break;
+                       }
+                       ret += copied;
+                       buf->offset = 0;
+                       buf->len = copied;
+
                        if (!iov_iter_count(from))
                                break;
                }
 
-               if (!pipe_full(head, tail, max_usage))
+               if (!pipe_full(head, pipe->tail, max_usage))
                        continue;
 
                /* Wait for buffer space to become available. */