string: factorize skip_spaces and export it to be generally available
[safe/jmp/linux-2.6] / fs / splice.c
index e845445..3920866 100644 (file)
@@ -9,33 +9,27 @@
  * that transfers data buffers to or from a pipe buffer.
  *
  * Named by Larry McVoy, original implementation from Linus, extended by
- * Jens to support splicing to files and fixing the initial implementation
- * bugs.
+ * Jens to support splicing to files, network, direct splicing, etc and
+ * fixing lots of bugs.
  *
- * Copyright (C) 2005 Jens Axboe <axboe@suse.de>
- * Copyright (C) 2005 Linus Torvalds <torvalds@osdl.org>
+ * Copyright (C) 2005-2006 Jens Axboe <axboe@kernel.dk>
+ * Copyright (C) 2005-2006 Linus Torvalds <torvalds@osdl.org>
+ * Copyright (C) 2006 Ingo Molnar <mingo@elte.hu>
  *
  */
 #include <linux/fs.h>
 #include <linux/file.h>
 #include <linux/pagemap.h>
-#include <linux/pipe_fs_i.h>
+#include <linux/splice.h>
+#include <linux/memcontrol.h>
 #include <linux/mm_inline.h>
 #include <linux/swap.h>
 #include <linux/writeback.h>
 #include <linux/buffer_head.h>
 #include <linux/module.h>
 #include <linux/syscalls.h>
-
-/*
- * Passed to the actors
- */
-struct splice_desc {
-       unsigned int len, total_len;    /* current and remaining length */
-       unsigned int flags;             /* splice flags */
-       struct file *file;              /* file to read/write */
-       loff_t pos;                     /* file position */
-};
+#include <linux/uio.h>
+#include <linux/security.h>
 
 /*
  * Attempt to steal a page from a pipe buffer. This should perhaps go into
@@ -43,137 +37,189 @@ struct splice_desc {
  * addition of remove_mapping(). If success is returned, the caller may
  * attempt to reuse this page for another destination.
  */
-static int page_cache_pipe_buf_steal(struct pipe_inode_info *info,
+static int page_cache_pipe_buf_steal(struct pipe_inode_info *pipe,
                                     struct pipe_buffer *buf)
 {
        struct page *page = buf->page;
-       struct address_space *mapping = page_mapping(page);
+       struct address_space *mapping;
 
-       WARN_ON(!PageLocked(page));
-       WARN_ON(!PageUptodate(page));
+       lock_page(page);
 
-       if (PagePrivate(page))
-               try_to_release_page(page, mapping_gfp_mask(mapping));
+       mapping = page_mapping(page);
+       if (mapping) {
+               WARN_ON(!PageUptodate(page));
 
-       if (!remove_mapping(mapping, page))
-               return 1;
+               /*
+                * At least for ext2 with nobh option, we need to wait on
+                * writeback completing on this page, since we'll remove it
+                * from the pagecache.  Otherwise truncate wont wait on the
+                * page, allowing the disk blocks to be reused by someone else
+                * before we actually wrote our data to them. fs corruption
+                * ensues.
+                */
+               wait_on_page_writeback(page);
 
-       if (PageLRU(page)) {
-               struct zone *zone = page_zone(page);
+               if (page_has_private(page) &&
+                   !try_to_release_page(page, GFP_KERNEL))
+                       goto out_unlock;
 
-               spin_lock_irq(&zone->lru_lock);
-               BUG_ON(!PageLRU(page));
-               __ClearPageLRU(page);
-               del_page_from_lru(zone, page);
-               spin_unlock_irq(&zone->lru_lock);
+               /*
+                * If we succeeded in removing the mapping, set LRU flag
+                * and return good.
+                */
+               if (remove_mapping(mapping, page)) {
+                       buf->flags |= PIPE_BUF_FLAG_LRU;
+                       return 0;
+               }
        }
 
-       return 0;
+       /*
+        * Raced with truncate or failed to remove page from current
+        * address space, unlock and return failure.
+        */
+out_unlock:
+       unlock_page(page);
+       return 1;
 }
 
-static void page_cache_pipe_buf_release(struct pipe_inode_info *info,
+static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe,
                                        struct pipe_buffer *buf)
 {
        page_cache_release(buf->page);
-       buf->page = NULL;
+       buf->flags &= ~PIPE_BUF_FLAG_LRU;
 }
 
-static void *page_cache_pipe_buf_map(struct file *file,
-                                    struct pipe_inode_info *info,
-                                    struct pipe_buffer *buf)
+/*
+ * Check whether the contents of buf is OK to access. Since the content
+ * is a page cache page, IO may be in flight.
+ */
+static int page_cache_pipe_buf_confirm(struct pipe_inode_info *pipe,
+                                      struct pipe_buffer *buf)
 {
        struct page *page = buf->page;
-
-       lock_page(page);
+       int err;
 
        if (!PageUptodate(page)) {
-               unlock_page(page);
-               return ERR_PTR(-EIO);
-       }
+               lock_page(page);
+
+               /*
+                * Page got truncated/unhashed. This will cause a 0-byte
+                * splice, if this is the first page.
+                */
+               if (!page->mapping) {
+                       err = -ENODATA;
+                       goto error;
+               }
+
+               /*
+                * Uh oh, read-error from disk.
+                */
+               if (!PageUptodate(page)) {
+                       err = -EIO;
+                       goto error;
+               }
 
-       if (!page->mapping) {
+               /*
+                * Page is ok afterall, we are done.
+                */
                unlock_page(page);
-               return ERR_PTR(-ENODATA);
        }
 
-       return kmap(buf->page);
+       return 0;
+error:
+       unlock_page(page);
+       return err;
 }
 
-static void page_cache_pipe_buf_unmap(struct pipe_inode_info *info,
-                                     struct pipe_buffer *buf)
+static const struct pipe_buf_operations page_cache_pipe_buf_ops = {
+       .can_merge = 0,
+       .map = generic_pipe_buf_map,
+       .unmap = generic_pipe_buf_unmap,
+       .confirm = page_cache_pipe_buf_confirm,
+       .release = page_cache_pipe_buf_release,
+       .steal = page_cache_pipe_buf_steal,
+       .get = generic_pipe_buf_get,
+};
+
+static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe,
+                                   struct pipe_buffer *buf)
 {
-       unlock_page(buf->page);
-       kunmap(buf->page);
+       if (!(buf->flags & PIPE_BUF_FLAG_GIFT))
+               return 1;
+
+       buf->flags |= PIPE_BUF_FLAG_LRU;
+       return generic_pipe_buf_steal(pipe, buf);
 }
 
-static struct pipe_buf_operations page_cache_pipe_buf_ops = {
+static const struct pipe_buf_operations user_page_pipe_buf_ops = {
        .can_merge = 0,
-       .map = page_cache_pipe_buf_map,
-       .unmap = page_cache_pipe_buf_unmap,
+       .map = generic_pipe_buf_map,
+       .unmap = generic_pipe_buf_unmap,
+       .confirm = generic_pipe_buf_confirm,
        .release = page_cache_pipe_buf_release,
-       .steal = page_cache_pipe_buf_steal,
+       .steal = user_page_pipe_buf_steal,
+       .get = generic_pipe_buf_get,
 };
 
-/*
- * Pipe output worker. This sets up our pipe format with the page cache
- * pipe buffer operations. Otherwise very similar to the regular pipe_writev().
+/**
+ * splice_to_pipe - fill passed data into a pipe
+ * @pipe:      pipe to fill
+ * @spd:       data to fill
+ *
+ * Description:
+ *    @spd contains a map of pages and len/offset tuples, along with
+ *    the struct pipe_buf_operations associated with these pages. This
+ *    function will link that data to the pipe.
+ *
  */
-static ssize_t move_to_pipe(struct inode *inode, struct page **pages,
-                           int nr_pages, unsigned long offset,
-                           unsigned long len, unsigned int flags)
+ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
+                      struct splice_pipe_desc *spd)
 {
-       struct pipe_inode_info *info;
-       int ret, do_wakeup, i;
+       unsigned int spd_pages = spd->nr_pages;
+       int ret, do_wakeup, page_nr;
 
        ret = 0;
        do_wakeup = 0;
-       i = 0;
+       page_nr = 0;
 
-       mutex_lock(PIPE_MUTEX(*inode));
+       pipe_lock(pipe);
 
-       info = inode->i_pipe;
        for (;;) {
-               int bufs;
-
-               if (!PIPE_READERS(*inode)) {
+               if (!pipe->readers) {
                        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 = pages[i++];
-                       unsigned long this_len;
-
-                       this_len = PAGE_CACHE_SIZE - offset;
-                       if (this_len > len)
-                               this_len = len;
-
-                       buf->page = page;
-                       buf->offset = offset;
-                       buf->len = this_len;
-                       buf->ops = &page_cache_pipe_buf_ops;
-                       info->nrbufs = ++bufs;
-                       do_wakeup = 1;
-
-                       ret += this_len;
-                       len -= this_len;
-                       offset = 0;
-                       if (!--nr_pages)
-                               break;
-                       if (!len)
+               if (pipe->nrbufs < PIPE_BUFFERS) {
+                       int newbuf = (pipe->curbuf + pipe->nrbufs) & (PIPE_BUFFERS - 1);
+                       struct pipe_buffer *buf = pipe->bufs + newbuf;
+
+                       buf->page = spd->pages[page_nr];
+                       buf->offset = spd->partial[page_nr].offset;
+                       buf->len = spd->partial[page_nr].len;
+                       buf->private = spd->partial[page_nr].private;
+                       buf->ops = spd->ops;
+                       if (spd->flags & SPLICE_F_GIFT)
+                               buf->flags |= PIPE_BUF_FLAG_GIFT;
+
+                       pipe->nrbufs++;
+                       page_nr++;
+                       ret += buf->len;
+
+                       if (pipe->inode)
+                               do_wakeup = 1;
+
+                       if (!--spd->nr_pages)
                                break;
-                       if (bufs < PIPE_BUFFERS)
+                       if (pipe->nrbufs < PIPE_BUFFERS)
                                continue;
 
                        break;
                }
 
-               if (flags & SPLICE_F_NONBLOCK) {
+               if (spd->flags & SPLICE_F_NONBLOCK) {
                        if (!ret)
                                ret = -EAGAIN;
                        break;
@@ -186,196 +232,430 @@ static ssize_t move_to_pipe(struct inode *inode, struct page **pages,
                }
 
                if (do_wakeup) {
-                       wake_up_interruptible_sync(PIPE_WAIT(*inode));
-                       kill_fasync(PIPE_FASYNC_READERS(*inode), SIGIO,
-                                   POLL_IN);
+                       smp_mb();
+                       if (waitqueue_active(&pipe->wait))
+                               wake_up_interruptible_sync(&pipe->wait);
+                       kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
                        do_wakeup = 0;
                }
 
-               PIPE_WAITING_WRITERS(*inode)++;
-               pipe_wait(inode);
-               PIPE_WAITING_WRITERS(*inode)--;
+               pipe->waiting_writers++;
+               pipe_wait(pipe);
+               pipe->waiting_writers--;
        }
 
-       mutex_unlock(PIPE_MUTEX(*inode));
+       pipe_unlock(pipe);
 
        if (do_wakeup) {
-               wake_up_interruptible(PIPE_WAIT(*inode));
-               kill_fasync(PIPE_FASYNC_READERS(*inode), SIGIO, POLL_IN);
+               smp_mb();
+               if (waitqueue_active(&pipe->wait))
+                       wake_up_interruptible(&pipe->wait);
+               kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
        }
 
-       while (i < nr_pages)
-               page_cache_release(pages[i++]);
+       while (page_nr < spd_pages)
+               spd->spd_release(spd, page_nr++);
 
        return ret;
 }
 
-static int __generic_file_splice_read(struct file *in, struct inode *pipe,
-                                     size_t len, unsigned int flags)
+static void spd_release_page(struct splice_pipe_desc *spd, unsigned int i)
+{
+       page_cache_release(spd->pages[i]);
+}
+
+static int
+__generic_file_splice_read(struct file *in, loff_t *ppos,
+                          struct pipe_inode_info *pipe, size_t len,
+                          unsigned int flags)
 {
        struct address_space *mapping = in->f_mapping;
-       unsigned int offset, nr_pages;
-       struct page *pages[PIPE_BUFFERS], *shadow[PIPE_BUFFERS];
+       unsigned int loff, nr_pages, req_pages;
+       struct page *pages[PIPE_BUFFERS];
+       struct partial_page partial[PIPE_BUFFERS];
        struct page *page;
-       pgoff_t index, pidx;
-       int i, j;
-
-       index = in->f_pos >> PAGE_CACHE_SHIFT;
-       offset = in->f_pos & ~PAGE_CACHE_MASK;
-       nr_pages = (len + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-
-       if (nr_pages > PIPE_BUFFERS)
-               nr_pages = PIPE_BUFFERS;
+       pgoff_t index, end_index;
+       loff_t isize;
+       int error, page_nr;
+       struct splice_pipe_desc spd = {
+               .pages = pages,
+               .partial = partial,
+               .flags = flags,
+               .ops = &page_cache_pipe_buf_ops,
+               .spd_release = spd_release_page,
+       };
+
+       index = *ppos >> PAGE_CACHE_SHIFT;
+       loff = *ppos & ~PAGE_CACHE_MASK;
+       req_pages = (len + loff + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       nr_pages = min(req_pages, (unsigned)PIPE_BUFFERS);
 
        /*
-        * initiate read-ahead on this page range
+        * Lookup the (hopefully) full range of pages we need.
         */
-       do_page_cache_readahead(mapping, in, index, nr_pages);
+       spd.nr_pages = find_get_pages_contig(mapping, index, nr_pages, pages);
+       index += spd.nr_pages;
 
        /*
-        * Get as many pages from the page cache as possible..
-        * Start IO on the page cache entries we create (we
-        * can assume that any pre-existing ones we find have
-        * already had IO started on them).
+        * If find_get_pages_contig() returned fewer pages than we needed,
+        * readahead/allocate the rest and fill in the holes.
         */
-       i = find_get_pages(mapping, index, nr_pages, pages);
+       if (spd.nr_pages < nr_pages)
+               page_cache_sync_readahead(mapping, &in->f_ra, in,
+                               index, req_pages - spd.nr_pages);
 
-       /*
-        * common case - we found all pages and they are contiguous,
-        * kick them off
-        */
-       if (i && (pages[i - 1]->index == index + i - 1))
-               goto splice_them;
+       error = 0;
+       while (spd.nr_pages < nr_pages) {
+               /*
+                * Page could be there, find_get_pages_contig() breaks on
+                * the first hole.
+                */
+               page = find_get_page(mapping, index);
+               if (!page) {
+                       /*
+                        * page didn't exist, allocate one.
+                        */
+                       page = page_cache_alloc_cold(mapping);
+                       if (!page)
+                               break;
 
-       /*
-        * fill shadow[] with pages at the right locations, so we only
-        * have to fill holes
-        */
-       memset(shadow, 0, nr_pages * sizeof(struct page *));
-       for (j = 0; j < i; j++)
-               shadow[pages[j]->index - index] = pages[j];
+                       error = add_to_page_cache_lru(page, mapping, index,
+                                               mapping_gfp_mask(mapping));
+                       if (unlikely(error)) {
+                               page_cache_release(page);
+                               if (error == -EEXIST)
+                                       continue;
+                               break;
+                       }
+                       /*
+                        * add_to_page_cache() locks the page, unlock it
+                        * to avoid convoluting the logic below even more.
+                        */
+                       unlock_page(page);
+               }
+
+               pages[spd.nr_pages++] = page;
+               index++;
+       }
 
        /*
-        * now fill in the holes
+        * Now loop over the map and see if we need to start IO on any
+        * pages, fill in the partial map, etc.
         */
-       for (i = 0, pidx = index; i < nr_pages; pidx++, i++) {
-               int error;
+       index = *ppos >> PAGE_CACHE_SHIFT;
+       nr_pages = spd.nr_pages;
+       spd.nr_pages = 0;
+       for (page_nr = 0; page_nr < nr_pages; page_nr++) {
+               unsigned int this_len;
 
-               if (shadow[i])
-                       continue;
+               if (!len)
+                       break;
 
                /*
-                * no page there, look one up / create it
+                * this_len is the max we'll use from this page
                 */
-               page = find_or_create_page(mapping, pidx,
-                                                  mapping_gfp_mask(mapping));
-               if (!page)
-                       break;
+               this_len = min_t(unsigned long, len, PAGE_CACHE_SIZE - loff);
+               page = pages[page_nr];
 
-               if (PageUptodate(page))
-                       unlock_page(page);
-               else {
-                       error = mapping->a_ops->readpage(in, page);
+               if (PageReadahead(page))
+                       page_cache_async_readahead(mapping, &in->f_ra, in,
+                                       page, index, req_pages - page_nr);
+
+               /*
+                * If the page isn't uptodate, we may need to start io on it
+                */
+               if (!PageUptodate(page)) {
+                       /*
+                        * If in nonblock mode then dont block on waiting
+                        * for an in-flight io page
+                        */
+                       if (flags & SPLICE_F_NONBLOCK) {
+                               if (!trylock_page(page)) {
+                                       error = -EAGAIN;
+                                       break;
+                               }
+                       } else
+                               lock_page(page);
+
+                       /*
+                        * Page was truncated, or invalidated by the
+                        * filesystem.  Redo the find/create, but this time the
+                        * page is kept locked, so there's no chance of another
+                        * race with truncate/invalidate.
+                        */
+                       if (!page->mapping) {
+                               unlock_page(page);
+                               page = find_or_create_page(mapping, index,
+                                               mapping_gfp_mask(mapping));
+
+                               if (!page) {
+                                       error = -ENOMEM;
+                                       break;
+                               }
+                               page_cache_release(pages[page_nr]);
+                               pages[page_nr] = page;
+                       }
+                       /*
+                        * page was already under io and is now done, great
+                        */
+                       if (PageUptodate(page)) {
+                               unlock_page(page);
+                               goto fill_it;
+                       }
 
+                       /*
+                        * need to read in the page
+                        */
+                       error = mapping->a_ops->readpage(in, page);
                        if (unlikely(error)) {
-                               page_cache_release(page);
+                               /*
+                                * We really should re-lookup the page here,
+                                * but it complicates things a lot. Instead
+                                * lets just do what we already stored, and
+                                * we'll get it the next time we are called.
+                                */
+                               if (error == AOP_TRUNCATED_PAGE)
+                                       error = 0;
+
                                break;
                        }
                }
-               shadow[i] = page;
-       }
+fill_it:
+               /*
+                * i_size must be checked after PageUptodate.
+                */
+               isize = i_size_read(mapping->host);
+               end_index = (isize - 1) >> PAGE_CACHE_SHIFT;
+               if (unlikely(!isize || index > end_index))
+                       break;
+
+               /*
+                * if this is the last page, see if we need to shrink
+                * the length and stop
+                */
+               if (end_index == index) {
+                       unsigned int plen;
+
+                       /*
+                        * max good bytes in this page
+                        */
+                       plen = ((isize - 1) & ~PAGE_CACHE_MASK) + 1;
+                       if (plen <= loff)
+                               break;
 
-       if (!i) {
-               for (i = 0; i < nr_pages; i++) {
-                        if (shadow[i])
-                               page_cache_release(shadow[i]);
+                       /*
+                        * force quit after adding this page
+                        */
+                       this_len = min(this_len, plen - loff);
+                       len = this_len;
                }
-               return 0;
-       }
 
-       memcpy(pages, shadow, i * sizeof(struct page *));
+               partial[page_nr].offset = loff;
+               partial[page_nr].len = this_len;
+               len -= this_len;
+               loff = 0;
+               spd.nr_pages++;
+               index++;
+       }
 
        /*
-        * Now we splice them into the pipe..
+        * Release any pages at the end, if we quit early. 'page_nr' is how far
+        * we got, 'nr_pages' is how many pages are in the map.
         */
-splice_them:
-       return move_to_pipe(pipe, pages, i, offset, len, flags);
+       while (page_nr < nr_pages)
+               page_cache_release(pages[page_nr++]);
+       in->f_ra.prev_pos = (loff_t)index << PAGE_CACHE_SHIFT;
+
+       if (spd.nr_pages)
+               return splice_to_pipe(pipe, &spd);
+
+       return error;
 }
 
 /**
  * generic_file_splice_read - splice data from file to a pipe
  * @in:                file to splice from
+ * @ppos:      position in @in
  * @pipe:      pipe to splice to
  * @len:       number of bytes to splice
  * @flags:     splice modifier flags
  *
- * Will read pages from given file and fill them into a pipe.
+ * Description:
+ *    Will read pages from given file and fill them into a pipe. Can be
+ *    used as long as the address_space operations for the source implements
+ *    a readpage() hook.
  *
  */
-ssize_t generic_file_splice_read(struct file *in, struct inode *pipe,
-                                size_t len, unsigned int flags)
+ssize_t generic_file_splice_read(struct file *in, loff_t *ppos,
+                                struct pipe_inode_info *pipe, size_t len,
+                                unsigned int flags)
 {
-       ssize_t spliced;
+       loff_t isize, left;
        int ret;
 
-       ret = 0;
-       spliced = 0;
-       while (len) {
-               ret = __generic_file_splice_read(in, pipe, len, flags);
+       isize = i_size_read(in->f_mapping->host);
+       if (unlikely(*ppos >= isize))
+               return 0;
 
-               if (ret <= 0)
-                       break;
+       left = isize - *ppos;
+       if (unlikely(left < len))
+               len = left;
 
-               in->f_pos += ret;
-               len -= ret;
-               spliced += ret;
+       ret = __generic_file_splice_read(in, ppos, pipe, len, flags);
+       if (ret > 0) {
+               *ppos += ret;
+               file_accessed(in);
+       }
 
-               if (!(flags & SPLICE_F_NONBLOCK))
-                       continue;
-               ret = -EAGAIN;
-               break;
+       return ret;
+}
+EXPORT_SYMBOL(generic_file_splice_read);
+
+static const struct pipe_buf_operations default_pipe_buf_ops = {
+       .can_merge = 0,
+       .map = generic_pipe_buf_map,
+       .unmap = generic_pipe_buf_unmap,
+       .confirm = generic_pipe_buf_confirm,
+       .release = generic_pipe_buf_release,
+       .steal = generic_pipe_buf_steal,
+       .get = generic_pipe_buf_get,
+};
+
+static ssize_t kernel_readv(struct file *file, const struct iovec *vec,
+                           unsigned long vlen, loff_t offset)
+{
+       mm_segment_t old_fs;
+       loff_t pos = offset;
+       ssize_t res;
+
+       old_fs = get_fs();
+       set_fs(get_ds());
+       /* The cast to a user pointer is valid due to the set_fs() */
+       res = vfs_readv(file, (const struct iovec __user *)vec, vlen, &pos);
+       set_fs(old_fs);
+
+       return res;
+}
+
+static ssize_t kernel_write(struct file *file, const char *buf, size_t count,
+                           loff_t pos)
+{
+       mm_segment_t old_fs;
+       ssize_t res;
+
+       old_fs = get_fs();
+       set_fs(get_ds());
+       /* The cast to a user pointer is valid due to the set_fs() */
+       res = vfs_write(file, (const char __user *)buf, count, &pos);
+       set_fs(old_fs);
+
+       return res;
+}
+
+ssize_t default_file_splice_read(struct file *in, loff_t *ppos,
+                                struct pipe_inode_info *pipe, size_t len,
+                                unsigned int flags)
+{
+       unsigned int nr_pages;
+       unsigned int nr_freed;
+       size_t offset;
+       struct page *pages[PIPE_BUFFERS];
+       struct partial_page partial[PIPE_BUFFERS];
+       struct iovec vec[PIPE_BUFFERS];
+       pgoff_t index;
+       ssize_t res;
+       size_t this_len;
+       int error;
+       int i;
+       struct splice_pipe_desc spd = {
+               .pages = pages,
+               .partial = partial,
+               .flags = flags,
+               .ops = &default_pipe_buf_ops,
+               .spd_release = spd_release_page,
+       };
+
+       index = *ppos >> PAGE_CACHE_SHIFT;
+       offset = *ppos & ~PAGE_CACHE_MASK;
+       nr_pages = (len + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+
+       for (i = 0; i < nr_pages && i < PIPE_BUFFERS && len; i++) {
+               struct page *page;
+
+               page = alloc_page(GFP_USER);
+               error = -ENOMEM;
+               if (!page)
+                       goto err;
+
+               this_len = min_t(size_t, len, PAGE_CACHE_SIZE - offset);
+               vec[i].iov_base = (void __user *) page_address(page);
+               vec[i].iov_len = this_len;
+               pages[i] = page;
+               spd.nr_pages++;
+               len -= this_len;
+               offset = 0;
+       }
+
+       res = kernel_readv(in, vec, spd.nr_pages, *ppos);
+       if (res < 0) {
+               error = res;
+               goto err;
        }
 
-       if (spliced)
-               return spliced;
+       error = 0;
+       if (!res)
+               goto err;
+
+       nr_freed = 0;
+       for (i = 0; i < spd.nr_pages; i++) {
+               this_len = min_t(size_t, vec[i].iov_len, res);
+               partial[i].offset = 0;
+               partial[i].len = this_len;
+               if (!this_len) {
+                       __free_page(pages[i]);
+                       pages[i] = NULL;
+                       nr_freed++;
+               }
+               res -= this_len;
+       }
+       spd.nr_pages -= nr_freed;
 
-       return ret;
+       res = splice_to_pipe(pipe, &spd);
+       if (res > 0)
+               *ppos += res;
+
+       return res;
+
+err:
+       for (i = 0; i < spd.nr_pages; i++)
+               __free_page(pages[i]);
+
+       return error;
 }
+EXPORT_SYMBOL(default_file_splice_read);
 
 /*
  * Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos'
- * using sendpage().
+ * using sendpage(). Return the number of bytes sent.
  */
-static int pipe_to_sendpage(struct pipe_inode_info *info,
+static int pipe_to_sendpage(struct pipe_inode_info *pipe,
                            struct pipe_buffer *buf, struct splice_desc *sd)
 {
-       struct file *file = sd->file;
+       struct file *file = sd->u.file;
        loff_t pos = sd->pos;
-       unsigned int offset;
-       ssize_t ret;
-       void *ptr;
-       int more;
-
-       /*
-        * sub-optimal, but we are limited by the pipe ->map. we don't
-        * need a kmap'ed buffer here, we just want to make sure we
-        * have the page pinned if the pipe page originates from the
-        * page cache
-        */
-       ptr = buf->ops->map(file, info, buf);
-       if (IS_ERR(ptr))
-               return PTR_ERR(ptr);
-
-       offset = pos & ~PAGE_CACHE_MASK;
-       more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len;
-
-       ret = file->f_op->sendpage(file, buf->page, offset, sd->len, &pos,more);
-
-       buf->ops->unmap(info, buf);
-       if (ret == sd->len)
-               return 0;
+       int ret, more;
+
+       ret = buf->ops->confirm(pipe, buf);
+       if (!ret) {
+               more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len;
+               if (file->f_op && file->f_op->sendpage)
+                       ret = file->f_op->sendpage(file, buf->page, buf->offset,
+                                                  sd->len, &pos, more);
+               else
+                       ret = -EINVAL;
+       }
 
-       return -EIO;
+       return ret;
 }
 
 /*
@@ -398,356 +678,987 @@ static int pipe_to_sendpage(struct pipe_inode_info *info,
  * SPLICE_F_MOVE isn't set, or we cannot move the page, we simply create
  * a new page in the output file page cache and fill/dirty that.
  */
-static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
-                       struct splice_desc *sd)
+int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
+                struct splice_desc *sd)
 {
-       struct file *file = sd->file;
+       struct file *file = sd->u.file;
        struct address_space *mapping = file->f_mapping;
-       unsigned int offset;
+       unsigned int offset, this_len;
        struct page *page;
-       pgoff_t index;
-       char *src;
-       int ret, stolen;
+       void *fsdata;
+       int ret;
 
        /*
-        * after this, page will be locked and unmapped
+        * make sure the data in this buffer is uptodate
         */
-       src = buf->ops->map(file, info, buf);
-       if (IS_ERR(src))
-               return PTR_ERR(src);
+       ret = buf->ops->confirm(pipe, buf);
+       if (unlikely(ret))
+               return ret;
 
-       index = sd->pos >> PAGE_CACHE_SHIFT;
        offset = sd->pos & ~PAGE_CACHE_MASK;
-       stolen = 0;
-
-       /*
-        * reuse buf page, if SPLICE_F_MOVE is set
-        */
-       if (sd->flags & SPLICE_F_MOVE) {
-               /*
-                * If steal succeeds, buf->page is now pruned from the vm
-                * side (LRU and page cache) and we can reuse it.
-                */
-               if (buf->ops->steal(info, buf))
-                       goto find_page;
-
-               page = buf->page;
-               stolen = 1;
-               if (add_to_page_cache_lru(page, mapping, index,
-                                               mapping_gfp_mask(mapping)))
-                       goto find_page;
-       } else {
-find_page:
-               ret = -ENOMEM;
-               page = find_or_create_page(mapping, index,
-                                               mapping_gfp_mask(mapping));
-               if (!page)
-                       goto out;
-
-               /*
-                * If the page is uptodate, it is also locked. If it isn't
-                * uptodate, we can mark it uptodate if we are filling the
-                * full page. Otherwise we need to read it in first...
-                */
-               if (!PageUptodate(page)) {
-                       if (sd->len < PAGE_CACHE_SIZE) {
-                               ret = mapping->a_ops->readpage(file, page);
-                               if (unlikely(ret))
-                                       goto out;
-
-                               lock_page(page);
 
-                               if (!PageUptodate(page)) {
-                                       /*
-                                        * page got invalidated, repeat
-                                        */
-                                       if (!page->mapping) {
-                                               unlock_page(page);
-                                               page_cache_release(page);
-                                               goto find_page;
-                                       }
-                                       ret = -EIO;
-                                       goto out;
-                               }
-                       } else {
-                               WARN_ON(!PageLocked(page));
-                               SetPageUptodate(page);
-                       }
-               }
-       }
+       this_len = sd->len;
+       if (this_len + offset > PAGE_CACHE_SIZE)
+               this_len = PAGE_CACHE_SIZE - offset;
 
-       ret = mapping->a_ops->prepare_write(file, page, 0, sd->len);
-       if (ret == AOP_TRUNCATED_PAGE) {
-               page_cache_release(page);
-               goto find_page;
-       } else if (ret)
+       ret = pagecache_write_begin(file, mapping, sd->pos, this_len,
+                               AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata);
+       if (unlikely(ret))
                goto out;
 
-       if (!stolen) {
-               char *dst = kmap_atomic(page, KM_USER0);
+       if (buf->page != page) {
+               /*
+                * Careful, ->map() uses KM_USER0!
+                */
+               char *src = buf->ops->map(pipe, buf, 1);
+               char *dst = kmap_atomic(page, KM_USER1);
 
-               memcpy(dst + offset, src + buf->offset, sd->len);
+               memcpy(dst + offset, src + buf->offset, this_len);
                flush_dcache_page(page);
-               kunmap_atomic(dst, KM_USER0);
+               kunmap_atomic(dst, KM_USER1);
+               buf->ops->unmap(pipe, buf, src);
        }
-
-       ret = mapping->a_ops->commit_write(file, page, 0, sd->len);
-       if (ret == AOP_TRUNCATED_PAGE) {
-               page_cache_release(page);
-               goto find_page;
-       } else if (ret)
-               goto out;
-
-       balance_dirty_pages_ratelimited(mapping);
+       ret = pagecache_write_end(file, mapping, sd->pos, this_len, this_len,
+                               page, fsdata);
 out:
-       if (!stolen) {
-               page_cache_release(page);
-               unlock_page(page);
-       }
-       buf->ops->unmap(info, buf);
        return ret;
 }
+EXPORT_SYMBOL(pipe_to_file);
 
-typedef int (splice_actor)(struct pipe_inode_info *, struct pipe_buffer *,
-                          struct splice_desc *);
+static void wakeup_pipe_writers(struct pipe_inode_info *pipe)
+{
+       smp_mb();
+       if (waitqueue_active(&pipe->wait))
+               wake_up_interruptible(&pipe->wait);
+       kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
+}
 
-/*
- * Pipe input worker. Most of this logic works like a regular pipe, the
- * key here is the 'actor' worker passed in that actually moves the data
- * to the wanted destination. See pipe_to_file/pipe_to_sendpage above.
+/**
+ * splice_from_pipe_feed - feed available data from a pipe to a file
+ * @pipe:      pipe to splice from
+ * @sd:                information to @actor
+ * @actor:     handler that splices the data
+ *
+ * Description:
+ *    This function loops over the pipe and calls @actor to do the
+ *    actual moving of a single struct pipe_buffer to the desired
+ *    destination.  It returns when there's no more buffers left in
+ *    the pipe or if the requested number of bytes (@sd->total_len)
+ *    have been copied.  It returns a positive number (one) if the
+ *    pipe needs to be filled with more data, zero if the required
+ *    number of bytes have been copied and -errno on error.
+ *
+ *    This, together with splice_from_pipe_{begin,end,next}, may be
+ *    used to implement the functionality of __splice_from_pipe() when
+ *    locking is required around copying the pipe buffers to the
+ *    destination.
  */
-static ssize_t move_from_pipe(struct inode *inode, struct file *out,
-                             size_t len, unsigned int flags,
-                             splice_actor *actor)
+int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_desc *sd,
+                         splice_actor *actor)
 {
-       struct pipe_inode_info *info;
-       int ret, do_wakeup, err;
-       struct splice_desc sd;
-
-       ret = 0;
-       do_wakeup = 0;
-
-       sd.total_len = len;
-       sd.flags = flags;
-       sd.file = out;
-       sd.pos = out->f_pos;
-
-       mutex_lock(PIPE_MUTEX(*inode));
-
-       info = inode->i_pipe;
-       for (;;) {
-               int bufs = info->nrbufs;
+       int ret;
 
-               if (bufs) {
-                       int curbuf = info->curbuf;
-                       struct pipe_buffer *buf = info->bufs + curbuf;
-                       struct pipe_buf_operations *ops = buf->ops;
+       while (pipe->nrbufs) {
+               struct pipe_buffer *buf = pipe->bufs + pipe->curbuf;
+               const struct pipe_buf_operations *ops = buf->ops;
 
-                       sd.len = buf->len;
-                       if (sd.len > sd.total_len)
-                               sd.len = sd.total_len;
+               sd->len = buf->len;
+               if (sd->len > sd->total_len)
+                       sd->len = sd->total_len;
 
-                       err = actor(info, buf, &sd);
-                       if (err) {
-                               if (!ret && err != -ENODATA)
-                                       ret = err;
+               ret = actor(pipe, buf, sd);
+               if (ret <= 0) {
+                       if (ret == -ENODATA)
+                               ret = 0;
+                       return ret;
+               }
+               buf->offset += ret;
+               buf->len -= ret;
+
+               sd->num_spliced += ret;
+               sd->len -= ret;
+               sd->pos += ret;
+               sd->total_len -= ret;
+
+               if (!buf->len) {
+                       buf->ops = NULL;
+                       ops->release(pipe, buf);
+                       pipe->curbuf = (pipe->curbuf + 1) & (PIPE_BUFFERS - 1);
+                       pipe->nrbufs--;
+                       if (pipe->inode)
+                               sd->need_wakeup = true;
+               }
 
-                               break;
-                       }
+               if (!sd->total_len)
+                       return 0;
+       }
 
-                       ret += sd.len;
-                       buf->offset += sd.len;
-                       buf->len -= sd.len;
-                       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;
-                       }
+       return 1;
+}
+EXPORT_SYMBOL(splice_from_pipe_feed);
 
-                       sd.pos += sd.len;
-                       sd.total_len -= sd.len;
-                       if (!sd.total_len)
-                               break;
-               }
+/**
+ * splice_from_pipe_next - wait for some data to splice from
+ * @pipe:      pipe to splice from
+ * @sd:                information about the splice operation
+ *
+ * Description:
+ *    This function will wait for some data and return a positive
+ *    value (one) if pipe buffers are available.  It will return zero
+ *    or -errno if no more data needs to be spliced.
+ */
+int splice_from_pipe_next(struct pipe_inode_info *pipe, struct splice_desc *sd)
+{
+       while (!pipe->nrbufs) {
+               if (!pipe->writers)
+                       return 0;
 
-               if (bufs)
-                       continue;
-               if (!PIPE_WRITERS(*inode))
-                       break;
-               if (!PIPE_WAITING_WRITERS(*inode)) {
-                       if (ret)
-                               break;
-               }
+               if (!pipe->waiting_writers && sd->num_spliced)
+                       return 0;
 
-               if (flags & SPLICE_F_NONBLOCK) {
-                       if (!ret)
-                               ret = -EAGAIN;
-                       break;
-               }
+               if (sd->flags & SPLICE_F_NONBLOCK)
+                       return -EAGAIN;
 
-               if (signal_pending(current)) {
-                       if (!ret)
-                               ret = -ERESTARTSYS;
-                       break;
-               }
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
 
-               if (do_wakeup) {
-                       wake_up_interruptible_sync(PIPE_WAIT(*inode));
-                       kill_fasync(PIPE_FASYNC_WRITERS(*inode),SIGIO,POLL_OUT);
-                       do_wakeup = 0;
+               if (sd->need_wakeup) {
+                       wakeup_pipe_writers(pipe);
+                       sd->need_wakeup = false;
                }
 
-               pipe_wait(inode);
+               pipe_wait(pipe);
        }
 
-       mutex_unlock(PIPE_MUTEX(*inode));
-
-       if (do_wakeup) {
-               wake_up_interruptible(PIPE_WAIT(*inode));
-               kill_fasync(PIPE_FASYNC_WRITERS(*inode), SIGIO, POLL_OUT);
-       }
+       return 1;
+}
+EXPORT_SYMBOL(splice_from_pipe_next);
 
-       mutex_lock(&out->f_mapping->host->i_mutex);
-       out->f_pos = sd.pos;
-       mutex_unlock(&out->f_mapping->host->i_mutex);
-       return ret;
+/**
+ * splice_from_pipe_begin - start splicing from pipe
+ * @sd:                information about the splice operation
+ *
+ * Description:
+ *    This function should be called before a loop containing
+ *    splice_from_pipe_next() and splice_from_pipe_feed() to
+ *    initialize the necessary fields of @sd.
+ */
+void splice_from_pipe_begin(struct splice_desc *sd)
+{
+       sd->num_spliced = 0;
+       sd->need_wakeup = false;
+}
+EXPORT_SYMBOL(splice_from_pipe_begin);
 
+/**
+ * splice_from_pipe_end - finish splicing from pipe
+ * @pipe:      pipe to splice from
+ * @sd:                information about the splice operation
+ *
+ * Description:
+ *    This function will wake up pipe writers if necessary.  It should
+ *    be called after a loop containing splice_from_pipe_next() and
+ *    splice_from_pipe_feed().
+ */
+void splice_from_pipe_end(struct pipe_inode_info *pipe, struct splice_desc *sd)
+{
+       if (sd->need_wakeup)
+               wakeup_pipe_writers(pipe);
 }
+EXPORT_SYMBOL(splice_from_pipe_end);
 
 /**
- * generic_file_splice_write - splice data from a pipe to a file
- * @inode:     pipe inode
- * @out:       file to write to
- * @len:       number of bytes to splice
- * @flags:     splice modifier flags
+ * __splice_from_pipe - splice data from a pipe to given actor
+ * @pipe:      pipe to splice from
+ * @sd:                information to @actor
+ * @actor:     handler that splices the data
  *
- * Will either move or copy pages (determined by @flags options) from
- * the given pipe inode to the given file.
+ * Description:
+ *    This function does little more than loop over the pipe and call
+ *    @actor to do the actual moving of a single struct pipe_buffer to
+ *    the desired destination. See pipe_to_file, pipe_to_sendpage, or
+ *    pipe_to_user.
  *
  */
-ssize_t generic_file_splice_write(struct inode *inode, struct file *out,
-                                 size_t len, unsigned int flags)
+ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, struct splice_desc *sd,
+                          splice_actor *actor)
 {
-       struct address_space *mapping = out->f_mapping;
-       ssize_t ret = move_from_pipe(inode, out, len, flags, pipe_to_file);
+       int ret;
 
-       /*
-        * if file or inode is SYNC and we actually wrote some data, sync it
-        */
-       if (unlikely((out->f_flags & O_SYNC) || IS_SYNC(mapping->host))
-           && ret > 0) {
-               struct inode *inode = mapping->host;
-               int err;
+       splice_from_pipe_begin(sd);
+       do {
+               ret = splice_from_pipe_next(pipe, sd);
+               if (ret > 0)
+                       ret = splice_from_pipe_feed(pipe, sd, actor);
+       } while (ret > 0);
+       splice_from_pipe_end(pipe, sd);
+
+       return sd->num_spliced ? sd->num_spliced : ret;
+}
+EXPORT_SYMBOL(__splice_from_pipe);
+
+/**
+ * splice_from_pipe - splice data from a pipe to a file
+ * @pipe:      pipe to splice from
+ * @out:       file to splice to
+ * @ppos:      position in @out
+ * @len:       how many bytes to splice
+ * @flags:     splice modifier flags
+ * @actor:     handler that splices the data
+ *
+ * Description:
+ *    See __splice_from_pipe. This function locks the pipe inode,
+ *    otherwise it's identical to __splice_from_pipe().
+ *
+ */
+ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out,
+                        loff_t *ppos, size_t len, unsigned int flags,
+                        splice_actor *actor)
+{
+       ssize_t ret;
+       struct splice_desc sd = {
+               .total_len = len,
+               .flags = flags,
+               .pos = *ppos,
+               .u.file = out,
+       };
+
+       pipe_lock(pipe);
+       ret = __splice_from_pipe(pipe, &sd, actor);
+       pipe_unlock(pipe);
 
-               mutex_lock(&inode->i_mutex);
-               err = generic_osync_inode(mapping->host, mapping,
-                                               OSYNC_METADATA|OSYNC_DATA);
+       return ret;
+}
+
+/**
+ * generic_file_splice_write - splice data from a pipe to a file
+ * @pipe:      pipe info
+ * @out:       file to write to
+ * @ppos:      position in @out
+ * @len:       number of bytes to splice
+ * @flags:     splice modifier flags
+ *
+ * Description:
+ *    Will either move or copy pages (determined by @flags options) from
+ *    the given pipe inode to the given file.
+ *
+ */
+ssize_t
+generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
+                         loff_t *ppos, size_t len, unsigned int flags)
+{
+       struct address_space *mapping = out->f_mapping;
+       struct inode *inode = mapping->host;
+       struct splice_desc sd = {
+               .total_len = len,
+               .flags = flags,
+               .pos = *ppos,
+               .u.file = out,
+       };
+       ssize_t ret;
+
+       pipe_lock(pipe);
+
+       splice_from_pipe_begin(&sd);
+       do {
+               ret = splice_from_pipe_next(pipe, &sd);
+               if (ret <= 0)
+                       break;
+
+               mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
+               ret = file_remove_suid(out);
+               if (!ret) {
+                       file_update_time(out);
+                       ret = splice_from_pipe_feed(pipe, &sd, pipe_to_file);
+               }
                mutex_unlock(&inode->i_mutex);
+       } while (ret > 0);
+       splice_from_pipe_end(pipe, &sd);
+
+       pipe_unlock(pipe);
 
+       if (sd.num_spliced)
+               ret = sd.num_spliced;
+
+       if (ret > 0) {
+               unsigned long nr_pages;
+               int err;
+
+               nr_pages = (ret + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+
+               err = generic_write_sync(out, *ppos, ret);
                if (err)
                        ret = err;
+               else
+                       *ppos += ret;
+               balance_dirty_pages_ratelimited_nr(mapping, nr_pages);
        }
 
        return ret;
 }
 
+EXPORT_SYMBOL(generic_file_splice_write);
+
+static int write_pipe_buf(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
+                         struct splice_desc *sd)
+{
+       int ret;
+       void *data;
+
+       ret = buf->ops->confirm(pipe, buf);
+       if (ret)
+               return ret;
+
+       data = buf->ops->map(pipe, buf, 0);
+       ret = kernel_write(sd->u.file, data + buf->offset, sd->len, sd->pos);
+       buf->ops->unmap(pipe, buf, data);
+
+       return ret;
+}
+
+static ssize_t default_file_splice_write(struct pipe_inode_info *pipe,
+                                        struct file *out, loff_t *ppos,
+                                        size_t len, unsigned int flags)
+{
+       ssize_t ret;
+
+       ret = splice_from_pipe(pipe, out, ppos, len, flags, write_pipe_buf);
+       if (ret > 0)
+               *ppos += ret;
+
+       return ret;
+}
+
 /**
  * generic_splice_sendpage - splice data from a pipe to a socket
- * @inode:     pipe inode
+ * @pipe:      pipe to splice from
  * @out:       socket to write to
+ * @ppos:      position in @out
  * @len:       number of bytes to splice
  * @flags:     splice modifier flags
  *
- * Will send @len bytes from the pipe to a network socket. No data copying
- * is involved.
+ * Description:
+ *    Will send @len bytes from the pipe to a network socket. No data copying
+ *    is involved.
  *
  */
-ssize_t generic_splice_sendpage(struct inode *inode, struct file *out,
-                               size_t len, unsigned int flags)
+ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, struct file *out,
+                               loff_t *ppos, size_t len, unsigned int flags)
 {
-       return move_from_pipe(inode, out, len, flags, pipe_to_sendpage);
+       return splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_sendpage);
 }
 
-EXPORT_SYMBOL(generic_file_splice_write);
-EXPORT_SYMBOL(generic_file_splice_read);
+EXPORT_SYMBOL(generic_splice_sendpage);
 
 /*
  * Attempt to initiate a splice from pipe to file.
  */
-static long do_splice_from(struct inode *pipe, struct file *out, size_t len,
-                          unsigned int flags)
+static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
+                          loff_t *ppos, size_t len, unsigned int flags)
 {
-       loff_t pos;
+       ssize_t (*splice_write)(struct pipe_inode_info *, struct file *,
+                               loff_t *, size_t, unsigned int);
        int ret;
 
-       if (!out->f_op || !out->f_op->splice_write)
-               return -EINVAL;
-
-       if (!(out->f_mode & FMODE_WRITE))
+       if (unlikely(!(out->f_mode & FMODE_WRITE)))
                return -EBADF;
 
-       pos = out->f_pos;
-       ret = rw_verify_area(WRITE, out, &pos, len);
+       if (unlikely(out->f_flags & O_APPEND))
+               return -EINVAL;
+
+       ret = rw_verify_area(WRITE, out, ppos, len);
        if (unlikely(ret < 0))
                return ret;
 
-       return out->f_op->splice_write(pipe, out, len, flags);
+       if (out->f_op && out->f_op->splice_write)
+               splice_write = out->f_op->splice_write;
+       else
+               splice_write = default_file_splice_write;
+
+       return splice_write(pipe, out, ppos, len, flags);
 }
 
 /*
  * Attempt to initiate a splice from a file to a pipe.
  */
-static long do_splice_to(struct file *in, struct inode *pipe, size_t len,
+static long do_splice_to(struct file *in, loff_t *ppos,
+                        struct pipe_inode_info *pipe, size_t len,
                         unsigned int flags)
 {
-       loff_t pos, isize, left;
+       ssize_t (*splice_read)(struct file *, loff_t *,
+                              struct pipe_inode_info *, size_t, unsigned int);
        int ret;
 
-       if (!in->f_op || !in->f_op->splice_read)
-               return -EINVAL;
-
-       if (!(in->f_mode & FMODE_READ))
+       if (unlikely(!(in->f_mode & FMODE_READ)))
                return -EBADF;
 
-       pos = in->f_pos;
-       ret = rw_verify_area(READ, in, &pos, len);
+       ret = rw_verify_area(READ, in, ppos, len);
        if (unlikely(ret < 0))
                return ret;
 
-       isize = i_size_read(in->f_mapping->host);
-       if (unlikely(in->f_pos >= isize))
-               return 0;
-       
-       left = isize - in->f_pos;
-       if (left < len)
-               len = left;
+       if (in->f_op && in->f_op->splice_read)
+               splice_read = in->f_op->splice_read;
+       else
+               splice_read = default_file_splice_read;
+
+       return splice_read(in, ppos, pipe, len, flags);
+}
+
+/**
+ * splice_direct_to_actor - splices data directly between two non-pipes
+ * @in:                file to splice from
+ * @sd:                actor information on where to splice to
+ * @actor:     handles the data splicing
+ *
+ * Description:
+ *    This is a special case helper to splice directly between two
+ *    points, without requiring an explicit pipe. Internally an allocated
+ *    pipe is cached in the process, and reused during the lifetime of
+ *    that process.
+ *
+ */
+ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
+                              splice_direct_actor *actor)
+{
+       struct pipe_inode_info *pipe;
+       long ret, bytes;
+       umode_t i_mode;
+       size_t len;
+       int i, flags;
+
+       /*
+        * We require the input being a regular file, as we don't want to
+        * randomly drop data for eg socket -> socket splicing. Use the
+        * piped splicing for that!
+        */
+       i_mode = in->f_path.dentry->d_inode->i_mode;
+       if (unlikely(!S_ISREG(i_mode) && !S_ISBLK(i_mode)))
+               return -EINVAL;
+
+       /*
+        * neither in nor out is a pipe, setup an internal pipe attached to
+        * 'out' and transfer the wanted data from 'in' to 'out' through that
+        */
+       pipe = current->splice_pipe;
+       if (unlikely(!pipe)) {
+               pipe = alloc_pipe_info(NULL);
+               if (!pipe)
+                       return -ENOMEM;
+
+               /*
+                * We don't have an immediate reader, but we'll read the stuff
+                * out of the pipe right after the splice_to_pipe(). So set
+                * PIPE_READERS appropriately.
+                */
+               pipe->readers = 1;
+
+               current->splice_pipe = pipe;
+       }
+
+       /*
+        * Do the splice.
+        */
+       ret = 0;
+       bytes = 0;
+       len = sd->total_len;
+       flags = sd->flags;
+
+       /*
+        * Don't block on output, we have to drain the direct pipe.
+        */
+       sd->flags &= ~SPLICE_F_NONBLOCK;
+
+       while (len) {
+               size_t read_len;
+               loff_t pos = sd->pos, prev_pos = pos;
+
+               ret = do_splice_to(in, &pos, pipe, len, flags);
+               if (unlikely(ret <= 0))
+                       goto out_release;
+
+               read_len = ret;
+               sd->total_len = read_len;
+
+               /*
+                * NOTE: nonblocking mode only applies to the input. We
+                * must not do the output in nonblocking mode as then we
+                * could get stuck data in the internal pipe:
+                */
+               ret = actor(pipe, sd);
+               if (unlikely(ret <= 0)) {
+                       sd->pos = prev_pos;
+                       goto out_release;
+               }
+
+               bytes += ret;
+               len -= ret;
+               sd->pos = pos;
+
+               if (ret < read_len) {
+                       sd->pos = prev_pos + ret;
+                       goto out_release;
+               }
+       }
+
+done:
+       pipe->nrbufs = pipe->curbuf = 0;
+       file_accessed(in);
+       return bytes;
 
-       return in->f_op->splice_read(in, pipe, len, flags);
+out_release:
+       /*
+        * If we did an incomplete transfer we must release
+        * the pipe buffers in question:
+        */
+       for (i = 0; i < PIPE_BUFFERS; i++) {
+               struct pipe_buffer *buf = pipe->bufs + i;
+
+               if (buf->ops) {
+                       buf->ops->release(pipe, buf);
+                       buf->ops = NULL;
+               }
+       }
+
+       if (!bytes)
+               bytes = ret;
+
+       goto done;
+}
+EXPORT_SYMBOL(splice_direct_to_actor);
+
+static int direct_splice_actor(struct pipe_inode_info *pipe,
+                              struct splice_desc *sd)
+{
+       struct file *file = sd->u.file;
+
+       return do_splice_from(pipe, file, &sd->pos, sd->total_len, sd->flags);
+}
+
+/**
+ * do_splice_direct - splices data directly between two files
+ * @in:                file to splice from
+ * @ppos:      input file offset
+ * @out:       file to splice to
+ * @len:       number of bytes to splice
+ * @flags:     splice modifier flags
+ *
+ * Description:
+ *    For use by do_sendfile(). splice can easily emulate sendfile, but
+ *    doing it in the application would incur an extra system call
+ *    (splice in + splice out, as compared to just sendfile()). So this helper
+ *    can splice directly through a process-private pipe.
+ *
+ */
+long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
+                     size_t len, unsigned int flags)
+{
+       struct splice_desc sd = {
+               .len            = len,
+               .total_len      = len,
+               .flags          = flags,
+               .pos            = *ppos,
+               .u.file         = out,
+       };
+       long ret;
+
+       ret = splice_direct_to_actor(in, &sd, direct_splice_actor);
+       if (ret > 0)
+               *ppos = sd.pos;
+
+       return ret;
+}
+
+static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe,
+                              struct pipe_inode_info *opipe,
+                              size_t len, unsigned int flags);
+/*
+ * After the inode slimming patch, i_pipe/i_bdev/i_cdev share the same
+ * location, so checking ->i_pipe is not enough to verify that this is a
+ * pipe.
+ */
+static inline struct pipe_inode_info *pipe_info(struct inode *inode)
+{
+       if (S_ISFIFO(inode->i_mode))
+               return inode->i_pipe;
+
+       return NULL;
 }
 
 /*
  * Determine where to splice to/from.
  */
-static long do_splice(struct file *in, struct file *out, size_t len,
-                     unsigned int flags)
+static long do_splice(struct file *in, loff_t __user *off_in,
+                     struct file *out, loff_t __user *off_out,
+                     size_t len, unsigned int flags)
 {
-       struct inode *pipe;
+       struct pipe_inode_info *ipipe;
+       struct pipe_inode_info *opipe;
+       loff_t offset, *off;
+       long ret;
 
-       pipe = in->f_dentry->d_inode;
-       if (pipe->i_pipe)
-               return do_splice_from(pipe, out, len, flags);
+       ipipe = pipe_info(in->f_path.dentry->d_inode);
+       opipe = pipe_info(out->f_path.dentry->d_inode);
 
-       pipe = out->f_dentry->d_inode;
-       if (pipe->i_pipe)
-               return do_splice_to(in, pipe, len, flags);
+       if (ipipe && opipe) {
+               if (off_in || off_out)
+                       return -ESPIPE;
+
+               if (!(in->f_mode & FMODE_READ))
+                       return -EBADF;
+
+               if (!(out->f_mode & FMODE_WRITE))
+                       return -EBADF;
+
+               /* Splicing to self would be fun, but... */
+               if (ipipe == opipe)
+                       return -EINVAL;
+
+               return splice_pipe_to_pipe(ipipe, opipe, len, flags);
+       }
+
+       if (ipipe) {
+               if (off_in)
+                       return -ESPIPE;
+               if (off_out) {
+                       if (!out->f_op || !out->f_op->llseek ||
+                           out->f_op->llseek == no_llseek)
+                               return -EINVAL;
+                       if (copy_from_user(&offset, off_out, sizeof(loff_t)))
+                               return -EFAULT;
+                       off = &offset;
+               } else
+                       off = &out->f_pos;
+
+               ret = do_splice_from(ipipe, out, off, len, flags);
+
+               if (off_out && copy_to_user(off_out, off, sizeof(loff_t)))
+                       ret = -EFAULT;
+
+               return ret;
+       }
+
+       if (opipe) {
+               if (off_out)
+                       return -ESPIPE;
+               if (off_in) {
+                       if (!in->f_op || !in->f_op->llseek ||
+                           in->f_op->llseek == no_llseek)
+                               return -EINVAL;
+                       if (copy_from_user(&offset, off_in, sizeof(loff_t)))
+                               return -EFAULT;
+                       off = &offset;
+               } else
+                       off = &in->f_pos;
+
+               ret = do_splice_to(in, off, opipe, len, flags);
+
+               if (off_in && copy_to_user(off_in, off, sizeof(loff_t)))
+                       ret = -EFAULT;
+
+               return ret;
+       }
 
        return -EINVAL;
 }
 
-asmlinkage long sys_splice(int fdin, int fdout, size_t len, unsigned int flags)
+/*
+ * Map an iov into an array of pages and offset/length tupples. With the
+ * partial_page structure, we can map several non-contiguous ranges into
+ * our ones pages[] map instead of splitting that operation into pieces.
+ * Could easily be exported as a generic helper for other users, in which
+ * case one would probably want to add a 'max_nr_pages' parameter as well.
+ */
+static int get_iovec_page_array(const struct iovec __user *iov,
+                               unsigned int nr_vecs, struct page **pages,
+                               struct partial_page *partial, int aligned)
+{
+       int buffers = 0, error = 0;
+
+       while (nr_vecs) {
+               unsigned long off, npages;
+               struct iovec entry;
+               void __user *base;
+               size_t len;
+               int i;
+
+               error = -EFAULT;
+               if (copy_from_user(&entry, iov, sizeof(entry)))
+                       break;
+
+               base = entry.iov_base;
+               len = entry.iov_len;
+
+               /*
+                * Sanity check this iovec. 0 read succeeds.
+                */
+               error = 0;
+               if (unlikely(!len))
+                       break;
+               error = -EFAULT;
+               if (!access_ok(VERIFY_READ, base, len))
+                       break;
+
+               /*
+                * Get this base offset and number of pages, then map
+                * in the user pages.
+                */
+               off = (unsigned long) base & ~PAGE_MASK;
+
+               /*
+                * If asked for alignment, the offset must be zero and the
+                * length a multiple of the PAGE_SIZE.
+                */
+               error = -EINVAL;
+               if (aligned && (off || len & ~PAGE_MASK))
+                       break;
+
+               npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+               if (npages > PIPE_BUFFERS - buffers)
+                       npages = PIPE_BUFFERS - buffers;
+
+               error = get_user_pages_fast((unsigned long)base, npages,
+                                       0, &pages[buffers]);
+
+               if (unlikely(error <= 0))
+                       break;
+
+               /*
+                * Fill this contiguous range into the partial page map.
+                */
+               for (i = 0; i < error; i++) {
+                       const int plen = min_t(size_t, len, PAGE_SIZE - off);
+
+                       partial[buffers].offset = off;
+                       partial[buffers].len = plen;
+
+                       off = 0;
+                       len -= plen;
+                       buffers++;
+               }
+
+               /*
+                * We didn't complete this iov, stop here since it probably
+                * means we have to move some of this into a pipe to
+                * be able to continue.
+                */
+               if (len)
+                       break;
+
+               /*
+                * Don't continue if we mapped fewer pages than we asked for,
+                * or if we mapped the max number of pages that we have
+                * room for.
+                */
+               if (error < npages || buffers == PIPE_BUFFERS)
+                       break;
+
+               nr_vecs--;
+               iov++;
+       }
+
+       if (buffers)
+               return buffers;
+
+       return error;
+}
+
+static int pipe_to_user(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
+                       struct splice_desc *sd)
+{
+       char *src;
+       int ret;
+
+       ret = buf->ops->confirm(pipe, buf);
+       if (unlikely(ret))
+               return ret;
+
+       /*
+        * See if we can use the atomic maps, by prefaulting in the
+        * pages and doing an atomic copy
+        */
+       if (!fault_in_pages_writeable(sd->u.userptr, sd->len)) {
+               src = buf->ops->map(pipe, buf, 1);
+               ret = __copy_to_user_inatomic(sd->u.userptr, src + buf->offset,
+                                                       sd->len);
+               buf->ops->unmap(pipe, buf, src);
+               if (!ret) {
+                       ret = sd->len;
+                       goto out;
+               }
+       }
+
+       /*
+        * No dice, use slow non-atomic map and copy
+        */
+       src = buf->ops->map(pipe, buf, 0);
+
+       ret = sd->len;
+       if (copy_to_user(sd->u.userptr, src + buf->offset, sd->len))
+               ret = -EFAULT;
+
+       buf->ops->unmap(pipe, buf, src);
+out:
+       if (ret > 0)
+               sd->u.userptr += ret;
+       return ret;
+}
+
+/*
+ * For lack of a better implementation, implement vmsplice() to userspace
+ * as a simple copy of the pipes pages to the user iov.
+ */
+static long vmsplice_to_user(struct file *file, const struct iovec __user *iov,
+                            unsigned long nr_segs, unsigned int flags)
+{
+       struct pipe_inode_info *pipe;
+       struct splice_desc sd;
+       ssize_t size;
+       int error;
+       long ret;
+
+       pipe = pipe_info(file->f_path.dentry->d_inode);
+       if (!pipe)
+               return -EBADF;
+
+       pipe_lock(pipe);
+
+       error = ret = 0;
+       while (nr_segs) {
+               void __user *base;
+               size_t len;
+
+               /*
+                * Get user address base and length for this iovec.
+                */
+               error = get_user(base, &iov->iov_base);
+               if (unlikely(error))
+                       break;
+               error = get_user(len, &iov->iov_len);
+               if (unlikely(error))
+                       break;
+
+               /*
+                * Sanity check this iovec. 0 read succeeds.
+                */
+               if (unlikely(!len))
+                       break;
+               if (unlikely(!base)) {
+                       error = -EFAULT;
+                       break;
+               }
+
+               if (unlikely(!access_ok(VERIFY_WRITE, base, len))) {
+                       error = -EFAULT;
+                       break;
+               }
+
+               sd.len = 0;
+               sd.total_len = len;
+               sd.flags = flags;
+               sd.u.userptr = base;
+               sd.pos = 0;
+
+               size = __splice_from_pipe(pipe, &sd, pipe_to_user);
+               if (size < 0) {
+                       if (!ret)
+                               ret = size;
+
+                       break;
+               }
+
+               ret += size;
+
+               if (size < len)
+                       break;
+
+               nr_segs--;
+               iov++;
+       }
+
+       pipe_unlock(pipe);
+
+       if (!ret)
+               ret = error;
+
+       return ret;
+}
+
+/*
+ * vmsplice splices a user address range into a pipe. It can be thought of
+ * as splice-from-memory, where the regular splice is splice-from-file (or
+ * to file). In both cases the output is a pipe, naturally.
+ */
+static long vmsplice_to_pipe(struct file *file, const struct iovec __user *iov,
+                            unsigned long nr_segs, unsigned int flags)
+{
+       struct pipe_inode_info *pipe;
+       struct page *pages[PIPE_BUFFERS];
+       struct partial_page partial[PIPE_BUFFERS];
+       struct splice_pipe_desc spd = {
+               .pages = pages,
+               .partial = partial,
+               .flags = flags,
+               .ops = &user_page_pipe_buf_ops,
+               .spd_release = spd_release_page,
+       };
+
+       pipe = pipe_info(file->f_path.dentry->d_inode);
+       if (!pipe)
+               return -EBADF;
+
+       spd.nr_pages = get_iovec_page_array(iov, nr_segs, pages, partial,
+                                           flags & SPLICE_F_GIFT);
+       if (spd.nr_pages <= 0)
+               return spd.nr_pages;
+
+       return splice_to_pipe(pipe, &spd);
+}
+
+/*
+ * Note that vmsplice only really supports true splicing _from_ user memory
+ * to a pipe, not the other way around. Splicing from user memory is a simple
+ * operation that can be supported without any funky alignment restrictions
+ * or nasty vm tricks. We simply map in the user memory and fill them into
+ * a pipe. The reverse isn't quite as easy, though. There are two possible
+ * solutions for that:
+ *
+ *     - memcpy() the data internally, at which point we might as well just
+ *       do a regular read() on the buffer anyway.
+ *     - Lots of nasty vm tricks, that are neither fast nor flexible (it
+ *       has restriction limitations on both ends of the pipe).
+ *
+ * Currently we punt and implement it as a normal copy, see pipe_to_user().
+ *
+ */
+SYSCALL_DEFINE4(vmsplice, int, fd, const struct iovec __user *, iov,
+               unsigned long, nr_segs, unsigned int, flags)
+{
+       struct file *file;
+       long error;
+       int fput;
+
+       if (unlikely(nr_segs > UIO_MAXIOV))
+               return -EINVAL;
+       else if (unlikely(!nr_segs))
+               return 0;
+
+       error = -EBADF;
+       file = fget_light(fd, &fput);
+       if (file) {
+               if (file->f_mode & FMODE_WRITE)
+                       error = vmsplice_to_pipe(file, iov, nr_segs, flags);
+               else if (file->f_mode & FMODE_READ)
+                       error = vmsplice_to_user(file, iov, nr_segs, flags);
+
+               fput_light(file, fput);
+       }
+
+       return error;
+}
+
+SYSCALL_DEFINE6(splice, int, fd_in, loff_t __user *, off_in,
+               int, fd_out, loff_t __user *, off_out,
+               size_t, len, unsigned int, flags)
 {
        long error;
        struct file *in, *out;
@@ -757,13 +1668,15 @@ asmlinkage long sys_splice(int fdin, int fdout, size_t len, unsigned int flags)
                return 0;
 
        error = -EBADF;
-       in = fget_light(fdin, &fput_in);
+       in = fget_light(fd_in, &fput_in);
        if (in) {
                if (in->f_mode & FMODE_READ) {
-                       out = fget_light(fdout, &fput_out);
+                       out = fget_light(fd_out, &fput_out);
                        if (out) {
                                if (out->f_mode & FMODE_WRITE)
-                                       error = do_splice(in, out, len, flags);
+                                       error = do_splice(in, off_in,
+                                                         out, off_out,
+                                                         len, flags);
                                fput_light(out, fput_out);
                        }
                }
@@ -773,3 +1686,342 @@ asmlinkage long sys_splice(int fdin, int fdout, size_t len, unsigned int flags)
 
        return error;
 }
+
+/*
+ * Make sure there's data to read. Wait for input if we can, otherwise
+ * return an appropriate error.
+ */
+static int ipipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
+{
+       int ret;
+
+       /*
+        * Check ->nrbufs without the inode lock first. This function
+        * is speculative anyways, so missing one is ok.
+        */
+       if (pipe->nrbufs)
+               return 0;
+
+       ret = 0;
+       pipe_lock(pipe);
+
+       while (!pipe->nrbufs) {
+               if (signal_pending(current)) {
+                       ret = -ERESTARTSYS;
+                       break;
+               }
+               if (!pipe->writers)
+                       break;
+               if (!pipe->waiting_writers) {
+                       if (flags & SPLICE_F_NONBLOCK) {
+                               ret = -EAGAIN;
+                               break;
+                       }
+               }
+               pipe_wait(pipe);
+       }
+
+       pipe_unlock(pipe);
+       return ret;
+}
+
+/*
+ * Make sure there's writeable room. Wait for room if we can, otherwise
+ * return an appropriate error.
+ */
+static int opipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
+{
+       int ret;
+
+       /*
+        * Check ->nrbufs without the inode lock first. This function
+        * is speculative anyways, so missing one is ok.
+        */
+       if (pipe->nrbufs < PIPE_BUFFERS)
+               return 0;
+
+       ret = 0;
+       pipe_lock(pipe);
+
+       while (pipe->nrbufs >= PIPE_BUFFERS) {
+               if (!pipe->readers) {
+                       send_sig(SIGPIPE, current, 0);
+                       ret = -EPIPE;
+                       break;
+               }
+               if (flags & SPLICE_F_NONBLOCK) {
+                       ret = -EAGAIN;
+                       break;
+               }
+               if (signal_pending(current)) {
+                       ret = -ERESTARTSYS;
+                       break;
+               }
+               pipe->waiting_writers++;
+               pipe_wait(pipe);
+               pipe->waiting_writers--;
+       }
+
+       pipe_unlock(pipe);
+       return ret;
+}
+
+/*
+ * Splice contents of ipipe to opipe.
+ */
+static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe,
+                              struct pipe_inode_info *opipe,
+                              size_t len, unsigned int flags)
+{
+       struct pipe_buffer *ibuf, *obuf;
+       int ret = 0, nbuf;
+       bool input_wakeup = false;
+
+
+retry:
+       ret = ipipe_prep(ipipe, flags);
+       if (ret)
+               return ret;
+
+       ret = opipe_prep(opipe, flags);
+       if (ret)
+               return ret;
+
+       /*
+        * Potential ABBA deadlock, work around it by ordering lock
+        * grabbing by pipe info address. Otherwise two different processes
+        * could deadlock (one doing tee from A -> B, the other from B -> A).
+        */
+       pipe_double_lock(ipipe, opipe);
+
+       do {
+               if (!opipe->readers) {
+                       send_sig(SIGPIPE, current, 0);
+                       if (!ret)
+                               ret = -EPIPE;
+                       break;
+               }
+
+               if (!ipipe->nrbufs && !ipipe->writers)
+                       break;
+
+               /*
+                * Cannot make any progress, because either the input
+                * pipe is empty or the output pipe is full.
+                */
+               if (!ipipe->nrbufs || opipe->nrbufs >= PIPE_BUFFERS) {
+                       /* Already processed some buffers, break */
+                       if (ret)
+                               break;
+
+                       if (flags & SPLICE_F_NONBLOCK) {
+                               ret = -EAGAIN;
+                               break;
+                       }
+
+                       /*
+                        * We raced with another reader/writer and haven't
+                        * managed to process any buffers.  A zero return
+                        * value means EOF, so retry instead.
+                        */
+                       pipe_unlock(ipipe);
+                       pipe_unlock(opipe);
+                       goto retry;
+               }
+
+               ibuf = ipipe->bufs + ipipe->curbuf;
+               nbuf = (opipe->curbuf + opipe->nrbufs) % PIPE_BUFFERS;
+               obuf = opipe->bufs + nbuf;
+
+               if (len >= ibuf->len) {
+                       /*
+                        * Simply move the whole buffer from ipipe to opipe
+                        */
+                       *obuf = *ibuf;
+                       ibuf->ops = NULL;
+                       opipe->nrbufs++;
+                       ipipe->curbuf = (ipipe->curbuf + 1) % PIPE_BUFFERS;
+                       ipipe->nrbufs--;
+                       input_wakeup = true;
+               } else {
+                       /*
+                        * Get a reference to this pipe buffer,
+                        * so we can copy the contents over.
+                        */
+                       ibuf->ops->get(ipipe, ibuf);
+                       *obuf = *ibuf;
+
+                       /*
+                        * Don't inherit the gift flag, we need to
+                        * prevent multiple steals of this page.
+                        */
+                       obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
+
+                       obuf->len = len;
+                       opipe->nrbufs++;
+                       ibuf->offset += obuf->len;
+                       ibuf->len -= obuf->len;
+               }
+               ret += obuf->len;
+               len -= obuf->len;
+       } while (len);
+
+       pipe_unlock(ipipe);
+       pipe_unlock(opipe);
+
+       /*
+        * If we put data in the output pipe, wakeup any potential readers.
+        */
+       if (ret > 0) {
+               smp_mb();
+               if (waitqueue_active(&opipe->wait))
+                       wake_up_interruptible(&opipe->wait);
+               kill_fasync(&opipe->fasync_readers, SIGIO, POLL_IN);
+       }
+       if (input_wakeup)
+               wakeup_pipe_writers(ipipe);
+
+       return ret;
+}
+
+/*
+ * Link contents of ipipe to opipe.
+ */
+static int link_pipe(struct pipe_inode_info *ipipe,
+                    struct pipe_inode_info *opipe,
+                    size_t len, unsigned int flags)
+{
+       struct pipe_buffer *ibuf, *obuf;
+       int ret = 0, i = 0, nbuf;
+
+       /*
+        * Potential ABBA deadlock, work around it by ordering lock
+        * grabbing by pipe info address. Otherwise two different processes
+        * could deadlock (one doing tee from A -> B, the other from B -> A).
+        */
+       pipe_double_lock(ipipe, opipe);
+
+       do {
+               if (!opipe->readers) {
+                       send_sig(SIGPIPE, current, 0);
+                       if (!ret)
+                               ret = -EPIPE;
+                       break;
+               }
+
+               /*
+                * If we have iterated all input buffers or ran out of
+                * output room, break.
+                */
+               if (i >= ipipe->nrbufs || opipe->nrbufs >= PIPE_BUFFERS)
+                       break;
+
+               ibuf = ipipe->bufs + ((ipipe->curbuf + i) & (PIPE_BUFFERS - 1));
+               nbuf = (opipe->curbuf + opipe->nrbufs) & (PIPE_BUFFERS - 1);
+
+               /*
+                * Get a reference to this pipe buffer,
+                * so we can copy the contents over.
+                */
+               ibuf->ops->get(ipipe, ibuf);
+
+               obuf = opipe->bufs + nbuf;
+               *obuf = *ibuf;
+
+               /*
+                * Don't inherit the gift flag, we need to
+                * prevent multiple steals of this page.
+                */
+               obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
+
+               if (obuf->len > len)
+                       obuf->len = len;
+
+               opipe->nrbufs++;
+               ret += obuf->len;
+               len -= obuf->len;
+               i++;
+       } while (len);
+
+       /*
+        * return EAGAIN if we have the potential of some data in the
+        * future, otherwise just return 0
+        */
+       if (!ret && ipipe->waiting_writers && (flags & SPLICE_F_NONBLOCK))
+               ret = -EAGAIN;
+
+       pipe_unlock(ipipe);
+       pipe_unlock(opipe);
+
+       /*
+        * If we put data in the output pipe, wakeup any potential readers.
+        */
+       if (ret > 0) {
+               smp_mb();
+               if (waitqueue_active(&opipe->wait))
+                       wake_up_interruptible(&opipe->wait);
+               kill_fasync(&opipe->fasync_readers, SIGIO, POLL_IN);
+       }
+
+       return ret;
+}
+
+/*
+ * This is a tee(1) implementation that works on pipes. It doesn't copy
+ * any data, it simply references the 'in' pages on the 'out' pipe.
+ * The 'flags' used are the SPLICE_F_* variants, currently the only
+ * applicable one is SPLICE_F_NONBLOCK.
+ */
+static long do_tee(struct file *in, struct file *out, size_t len,
+                  unsigned int flags)
+{
+       struct pipe_inode_info *ipipe = pipe_info(in->f_path.dentry->d_inode);
+       struct pipe_inode_info *opipe = pipe_info(out->f_path.dentry->d_inode);
+       int ret = -EINVAL;
+
+       /*
+        * Duplicate the contents of ipipe to opipe without actually
+        * copying the data.
+        */
+       if (ipipe && opipe && ipipe != opipe) {
+               /*
+                * Keep going, unless we encounter an error. The ipipe/opipe
+                * ordering doesn't really matter.
+                */
+               ret = ipipe_prep(ipipe, flags);
+               if (!ret) {
+                       ret = opipe_prep(opipe, flags);
+                       if (!ret)
+                               ret = link_pipe(ipipe, opipe, len, flags);
+               }
+       }
+
+       return ret;
+}
+
+SYSCALL_DEFINE4(tee, int, fdin, int, fdout, size_t, len, unsigned int, flags)
+{
+       struct file *in;
+       int error, fput_in;
+
+       if (unlikely(!len))
+               return 0;
+
+       error = -EBADF;
+       in = fget_light(fdin, &fput_in);
+       if (in) {
+               if (in->f_mode & FMODE_READ) {
+                       int fput_out;
+                       struct file *out = fget_light(fdout, &fput_out);
+
+                       if (out) {
+                               if (out->f_mode & FMODE_WRITE)
+                                       error = do_tee(in, out, len, flags);
+                               fput_light(out, fput_out);
+                       }
+               }
+               fput_light(in, fput_in);
+       }
+
+       return error;
+}