]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
.27 patches
authorGreg Kroah-Hartman <gregkh@suse.de>
Thu, 14 May 2009 03:52:43 +0000 (20:52 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 14 May 2009 03:52:43 +0000 (20:52 -0700)
queue-2.6.27/ehea-fix-invalid-pointer-access.patch [new file with mode: 0644]
queue-2.6.27/ocfs2-fix-i_mutex-locking-in-ocfs2_splice_to_file.patch [new file with mode: 0644]
queue-2.6.27/powerpc-5200-don-t-specify-irqf_shared-in-psc-uart-driver.patch [new file with mode: 0644]
queue-2.6.27/series
queue-2.6.27/splice-fix-i_mutex-locking-in-generic_splice_write.patch [new file with mode: 0644]
queue-2.6.27/splice-remove-i_mutex-locking-in-splice_from_pipe.patch [new file with mode: 0644]
queue-2.6.27/splice-split-up-__splice_from_pipe.patch [new file with mode: 0644]

diff --git a/queue-2.6.27/ehea-fix-invalid-pointer-access.patch b/queue-2.6.27/ehea-fix-invalid-pointer-access.patch
new file mode 100644 (file)
index 0000000..74ef9f8
--- /dev/null
@@ -0,0 +1,69 @@
+From 0b2febf38a33d7c40fb7bb4a58c113a1fa33c412 Mon Sep 17 00:00:00 2001
+From: Hannes Hering <hering2@de.ibm.com>
+Date: Mon, 4 May 2009 11:06:37 -0700
+Subject: ehea: fix invalid pointer access
+
+From: Hannes Hering <hering2@de.ibm.com>
+
+commit 0b2febf38a33d7c40fb7bb4a58c113a1fa33c412 upstream.
+
+This patch fixes an invalid pointer access in case the receive queue
+holds no pointer to the next skb when the queue is empty.
+
+Signed-off-by: Hannes Hering <hering2@de.ibm.com>
+Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/net/ehea/ehea_main.c |   31 ++++++++++++++++++-------------
+ 1 file changed, 18 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/ehea/ehea_main.c
++++ b/drivers/net/ehea/ehea_main.c
+@@ -529,14 +529,17 @@ static inline struct sk_buff *get_skb_by
+       x &= (arr_len - 1);
+       pref = skb_array[x];
+-      prefetchw(pref);
+-      prefetchw(pref + EHEA_CACHE_LINE);
++      if (pref) {
++              prefetchw(pref);
++              prefetchw(pref + EHEA_CACHE_LINE);
++
++              pref = (skb_array[x]->data);
++              prefetch(pref);
++              prefetch(pref + EHEA_CACHE_LINE);
++              prefetch(pref + EHEA_CACHE_LINE * 2);
++              prefetch(pref + EHEA_CACHE_LINE * 3);
++      }
+-      pref = (skb_array[x]->data);
+-      prefetch(pref);
+-      prefetch(pref + EHEA_CACHE_LINE);
+-      prefetch(pref + EHEA_CACHE_LINE * 2);
+-      prefetch(pref + EHEA_CACHE_LINE * 3);
+       skb = skb_array[skb_index];
+       skb_array[skb_index] = NULL;
+       return skb;
+@@ -553,12 +556,14 @@ static inline struct sk_buff *get_skb_by
+       x &= (arr_len - 1);
+       pref = skb_array[x];
+-      prefetchw(pref);
+-      prefetchw(pref + EHEA_CACHE_LINE);
+-
+-      pref = (skb_array[x]->data);
+-      prefetchw(pref);
+-      prefetchw(pref + EHEA_CACHE_LINE);
++      if (pref) {
++              prefetchw(pref);
++              prefetchw(pref + EHEA_CACHE_LINE);
++
++              pref = (skb_array[x]->data);
++              prefetchw(pref);
++              prefetchw(pref + EHEA_CACHE_LINE);
++      }
+       skb = skb_array[wqe_index];
+       skb_array[wqe_index] = NULL;
diff --git a/queue-2.6.27/ocfs2-fix-i_mutex-locking-in-ocfs2_splice_to_file.patch b/queue-2.6.27/ocfs2-fix-i_mutex-locking-in-ocfs2_splice_to_file.patch
new file mode 100644 (file)
index 0000000..b9aa379
--- /dev/null
@@ -0,0 +1,179 @@
+From 328eaaba4e41a04c1dc4679d65bea3fee4349d86 Mon Sep 17 00:00:00 2001
+From: Miklos Szeredi <miklos@szeredi.hu>
+Date: Tue, 14 Apr 2009 19:48:39 +0200
+Subject: ocfs2: fix i_mutex locking in ocfs2_splice_to_file()
+
+From: Miklos Szeredi <miklos@szeredi.hu>
+
+commit 328eaaba4e41a04c1dc4679d65bea3fee4349d86 upstream.
+
+Rearrange locking of i_mutex on destination and call to
+ocfs2_rw_lock() so locks are only held while buffers are copied with
+the pipe_to_file() actor, and not while waiting for more data on the
+pipe.
+
+Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
+Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/ocfs2/file.c        |   96 ++++++++++++++++++++++++++++++++++++++-----------
+ fs/splice.c            |    5 +-
+ include/linux/splice.h |    2 +
+ 3 files changed, 80 insertions(+), 23 deletions(-)
+
+--- a/fs/ocfs2/file.c
++++ b/fs/ocfs2/file.c
+@@ -2075,6 +2075,22 @@ out_sems:
+       return written ? written : ret;
+ }
++static int ocfs2_splice_to_file(struct pipe_inode_info *pipe,
++                              struct file *out,
++                              struct splice_desc *sd)
++{
++      int ret;
++
++      ret = ocfs2_prepare_inode_for_write(out->f_path.dentry, &sd->pos,
++                                          sd->total_len, 0, NULL);
++      if (ret < 0) {
++              mlog_errno(ret);
++              return ret;
++      }
++
++      return splice_from_pipe_feed(pipe, sd, pipe_to_file);
++}
++
+ static ssize_t ocfs2_file_splice_write(struct pipe_inode_info *pipe,
+                                      struct file *out,
+                                      loff_t *ppos,
+@@ -2082,38 +2098,76 @@ static ssize_t ocfs2_file_splice_write(s
+                                      unsigned int flags)
+ {
+       int ret;
+-      struct inode *inode = out->f_path.dentry->d_inode;
++      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,
++      };
+       mlog_entry("(0x%p, 0x%p, %u, '%.*s')\n", out, pipe,
+                  (unsigned int)len,
+                  out->f_path.dentry->d_name.len,
+                  out->f_path.dentry->d_name.name);
+-      mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
+-
+-      ret = ocfs2_rw_lock(inode, 1);
+-      if (ret < 0) {
+-              mlog_errno(ret);
+-              goto out;
+-      }
++      if (pipe->inode)
++              mutex_lock_nested(&pipe->inode->i_mutex, I_MUTEX_PARENT);
+-      ret = ocfs2_prepare_inode_for_write(out->f_path.dentry, ppos, len, 0,
+-                                          NULL);
+-      if (ret < 0) {
+-              mlog_errno(ret);
+-              goto out_unlock;
+-      }
++      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 = ocfs2_rw_lock(inode, 1);
++              if (ret < 0)
++                      mlog_errno(ret);
++              else {
++                      ret = ocfs2_splice_to_file(pipe, out, &sd);
++                      ocfs2_rw_unlock(inode, 1);
++              }
++              mutex_unlock(&inode->i_mutex);
++      } while (ret > 0);
++      splice_from_pipe_end(pipe, &sd);
+       if (pipe->inode)
+-              mutex_lock_nested(&pipe->inode->i_mutex, I_MUTEX_CHILD);
+-      ret = generic_file_splice_write_nolock(pipe, out, ppos, len, flags);
+-      if (pipe->inode)
+               mutex_unlock(&pipe->inode->i_mutex);
+-out_unlock:
+-      ocfs2_rw_unlock(inode, 1);
+-out:
+-      mutex_unlock(&inode->i_mutex);
++      if (sd.num_spliced)
++              ret = sd.num_spliced;
++
++      if (ret > 0) {
++              unsigned long nr_pages;
++
++              *ppos += ret;
++              nr_pages = (ret + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
++
++              /*
++               * If file or inode is SYNC and we actually wrote some data,
++               * sync it.
++               */
++              if (unlikely((out->f_flags & O_SYNC) || IS_SYNC(inode))) {
++                      int err;
++
++                      mutex_lock(&inode->i_mutex);
++                      err = ocfs2_rw_lock(inode, 1);
++                      if (err < 0) {
++                              mlog_errno(err);
++                      } else {
++                              err = generic_osync_inode(inode, mapping,
++                                                OSYNC_METADATA|OSYNC_DATA);
++                              ocfs2_rw_unlock(inode, 1);
++                      }
++                      mutex_unlock(&inode->i_mutex);
++
++                      if (err)
++                              ret = err;
++              }
++              balance_dirty_pages_ratelimited_nr(mapping, nr_pages);
++      }
+       mlog_exit(ret);
+       return ret;
+--- a/fs/splice.c
++++ b/fs/splice.c
+@@ -553,8 +553,8 @@ static int pipe_to_sendpage(struct pipe_
+  * 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 *pipe, 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->u.file;
+       struct address_space *mapping = file->f_mapping;
+@@ -598,6 +598,7 @@ static int pipe_to_file(struct pipe_inod
+ out:
+       return ret;
+ }
++EXPORT_SYMBOL(pipe_to_file);
+ static void wakeup_pipe_writers(struct pipe_inode_info *pipe)
+ {
+--- a/include/linux/splice.h
++++ b/include/linux/splice.h
+@@ -75,6 +75,8 @@ extern int splice_from_pipe_next(struct 
+ extern void splice_from_pipe_begin(struct splice_desc *);
+ extern void splice_from_pipe_end(struct pipe_inode_info *,
+                                struct splice_desc *);
++extern int pipe_to_file(struct pipe_inode_info *, struct pipe_buffer *,
++                      struct splice_desc *);
+ extern ssize_t splice_to_pipe(struct pipe_inode_info *,
+                             struct splice_pipe_desc *);
diff --git a/queue-2.6.27/powerpc-5200-don-t-specify-irqf_shared-in-psc-uart-driver.patch b/queue-2.6.27/powerpc-5200-don-t-specify-irqf_shared-in-psc-uart-driver.patch
new file mode 100644 (file)
index 0000000..640d3c5
--- /dev/null
@@ -0,0 +1,34 @@
+From d9f0c5f9bc74f16d0ea0f6c518b209e48783a796 Mon Sep 17 00:00:00 2001
+From: Grant Likely <grant.likely@secretlab.ca>
+Date: Wed, 4 Feb 2009 11:23:56 -0700
+Subject: powerpc/5200: Don't specify IRQF_SHARED in PSC UART driver
+
+From: Grant Likely <grant.likely@secretlab.ca>
+
+commit d9f0c5f9bc74f16d0ea0f6c518b209e48783a796 upstream.
+
+The MPC5200 PSC device is wired up to a dedicated interrupt line
+which is never shared.  This patch removes the IRQF_SHARED flag
+from the request_irq() call which eliminates the "IRQF_DISABLED
+is not guaranteed on shared IRQs" warning message from the console
+output.
+
+Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
+Reviewed-by: Wolfram Sang <w.sang@pengutronix.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/serial/mpc52xx_uart.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/serial/mpc52xx_uart.c
++++ b/drivers/serial/mpc52xx_uart.c
+@@ -515,7 +515,7 @@ mpc52xx_uart_startup(struct uart_port *p
+       /* Request IRQ */
+       ret = request_irq(port->irq, mpc52xx_uart_int,
+-              IRQF_DISABLED | IRQF_SAMPLE_RANDOM | IRQF_SHARED,
++              IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
+               "mpc52xx_psc_uart", port);
+       if (ret)
+               return ret;
index f116aa4078a609815ab1d828e9be112659ae9205..588c02c6965305fe92dc15ab9d68dccd0fb5a9f7 100644 (file)
@@ -20,3 +20,9 @@ cifs-fix-unicode-string-area-word-alignment-in-session-setup.patch
 epoll-fix-size-check-in-epoll_create.patch
 nfsd4-check-for-negative-dentry-before-use-in-nfsv4-readdir.patch
 nfs-fix-the-notifications-when-renaming-onto-an-existing-file.patch
+ehea-fix-invalid-pointer-access.patch
+powerpc-5200-don-t-specify-irqf_shared-in-psc-uart-driver.patch
+splice-split-up-__splice_from_pipe.patch
+splice-remove-i_mutex-locking-in-splice_from_pipe.patch
+splice-fix-i_mutex-locking-in-generic_splice_write.patch
+ocfs2-fix-i_mutex-locking-in-ocfs2_splice_to_file.patch
diff --git a/queue-2.6.27/splice-fix-i_mutex-locking-in-generic_splice_write.patch b/queue-2.6.27/splice-fix-i_mutex-locking-in-generic_splice_write.patch
new file mode 100644 (file)
index 0000000..c831ef4
--- /dev/null
@@ -0,0 +1,64 @@
+From eb443e5a25d43996deb62b9bcee1a4ce5dea2ead Mon Sep 17 00:00:00 2001
+From: Miklos Szeredi <miklos@szeredi.hu>
+Date: Tue, 14 Apr 2009 19:48:38 +0200
+Subject: splice: fix i_mutex locking in generic_splice_write()
+
+From: Miklos Szeredi <miklos@szeredi.hu>
+
+commit eb443e5a25d43996deb62b9bcee1a4ce5dea2ead upstream.
+
+Rearrange locking of i_mutex on destination so it's only held while
+buffers are copied with the pipe_to_file() actor, and not while
+waiting for more data on the pipe.
+
+Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
+Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/splice.c |   34 +++++++++++++++++++++++-----------
+ 1 file changed, 23 insertions(+), 11 deletions(-)
+
+--- a/fs/splice.c
++++ b/fs/splice.c
+@@ -893,17 +893,29 @@ generic_file_splice_write(struct pipe_in
+       };
+       ssize_t ret;
+-      WARN_ON(S_ISFIFO(inode->i_mode));
+-      mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
+-      ret = file_remove_suid(out);
+-      if (likely(!ret)) {
+-              if (pipe->inode)
+-                      mutex_lock_nested(&pipe->inode->i_mutex, I_MUTEX_CHILD);
+-              ret = __splice_from_pipe(pipe, &sd, pipe_to_file);
+-              if (pipe->inode)
+-                      mutex_unlock(&pipe->inode->i_mutex);
+-      }
+-      mutex_unlock(&inode->i_mutex);
++      if (pipe->inode)
++              mutex_lock_nested(&pipe->inode->i_mutex, I_MUTEX_PARENT);
++
++      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)
++                      ret = splice_from_pipe_feed(pipe, &sd, pipe_to_file);
++              mutex_unlock(&inode->i_mutex);
++      } while (ret > 0);
++      splice_from_pipe_end(pipe, &sd);
++
++      if (pipe->inode)
++              mutex_unlock(&pipe->inode->i_mutex);
++
++      if (sd.num_spliced)
++              ret = sd.num_spliced;
++
+       if (ret > 0) {
+               unsigned long nr_pages;
diff --git a/queue-2.6.27/splice-remove-i_mutex-locking-in-splice_from_pipe.patch b/queue-2.6.27/splice-remove-i_mutex-locking-in-splice_from_pipe.patch
new file mode 100644 (file)
index 0000000..1ddfb6b
--- /dev/null
@@ -0,0 +1,69 @@
+From 2933970b960223076d6affcf7a77e2bc546b8102 Mon Sep 17 00:00:00 2001
+From: Miklos Szeredi <miklos@szeredi.hu>
+Date: Tue, 14 Apr 2009 19:48:37 +0200
+Subject: splice: remove i_mutex locking in splice_from_pipe()
+
+From: Miklos Szeredi <miklos@szeredi.hu>
+
+commit 2933970b960223076d6affcf7a77e2bc546b8102 upstream.
+
+splice_from_pipe() is only called from two places:
+
+  - generic_splice_sendpage()
+  - splice_write_null()
+
+Neither of these require i_mutex to be taken on the destination inode.
+
+Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
+Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/splice.c |   18 ++----------------
+ 1 file changed, 2 insertions(+), 16 deletions(-)
+
+--- a/fs/splice.c
++++ b/fs/splice.c
+@@ -782,7 +782,7 @@ EXPORT_SYMBOL(__splice_from_pipe);
+  * @actor:    handler that splices the data
+  *
+  * Description:
+- *    See __splice_from_pipe. This function locks the input and output inodes,
++ *    See __splice_from_pipe. This function locks the pipe inode,
+  *    otherwise it's identical to __splice_from_pipe().
+  *
+  */
+@@ -791,7 +791,6 @@ ssize_t splice_from_pipe(struct pipe_ino
+                        splice_actor *actor)
+ {
+       ssize_t ret;
+-      struct inode *inode = out->f_mapping->host;
+       struct splice_desc sd = {
+               .total_len = len,
+               .flags = flags,
+@@ -799,24 +798,11 @@ ssize_t splice_from_pipe(struct pipe_ino
+               .u.file = out,
+       };
+-      /*
+-       * The actor worker might be calling ->prepare_write and
+-       * ->commit_write. Most of the time, these expect i_mutex to
+-       * be held. Since this may result in an ABBA deadlock with
+-       * pipe->inode, we have to order lock acquiry here.
+-       *
+-       * Outer lock must be inode->i_mutex, as pipe_wait() will
+-       * release and reacquire pipe->inode->i_mutex, AND inode must
+-       * never be a pipe.
+-       */
+-      WARN_ON(S_ISFIFO(inode->i_mode));
+-      mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
+       if (pipe->inode)
+-              mutex_lock_nested(&pipe->inode->i_mutex, I_MUTEX_CHILD);
++              mutex_lock(&pipe->inode->i_mutex);
+       ret = __splice_from_pipe(pipe, &sd, actor);
+       if (pipe->inode)
+               mutex_unlock(&pipe->inode->i_mutex);
+-      mutex_unlock(&inode->i_mutex);
+       return ret;
+ }
diff --git a/queue-2.6.27/splice-split-up-__splice_from_pipe.patch b/queue-2.6.27/splice-split-up-__splice_from_pipe.patch
new file mode 100644 (file)
index 0000000..e49b696
--- /dev/null
@@ -0,0 +1,317 @@
+From b3c2d2ddd63944ef2a1e4a43077b602288107e01 Mon Sep 17 00:00:00 2001
+From: Miklos Szeredi <miklos@szeredi.hu>
+Date: Tue, 14 Apr 2009 19:48:36 +0200
+Subject: splice: split up __splice_from_pipe()
+
+From: Miklos Szeredi <miklos@szeredi.hu>
+
+commit b3c2d2ddd63944ef2a1e4a43077b602288107e01 upstream.
+
+Split up __splice_from_pipe() into four helper functions:
+
+  splice_from_pipe_begin()
+  splice_from_pipe_next()
+  splice_from_pipe_feed()
+  splice_from_pipe_end()
+
+splice_from_pipe_next() will wait (if necessary) for more buffers to
+be added to the pipe.  splice_from_pipe_feed() will feed the buffers
+to the supplied actor and return when there's no more data available
+(or if all of the requested data has been copied).
+
+This is necessary so that implementations can do locking around the
+non-waiting splice_from_pipe_feed().
+
+This patch should not cause any change in behavior.
+
+Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
+Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/splice.c            |  223 ++++++++++++++++++++++++++++++++-----------------
+ include/linux/splice.h |   10 ++
+ 2 files changed, 156 insertions(+), 77 deletions(-)
+
+--- a/fs/splice.c
++++ b/fs/splice.c
+@@ -599,107 +599,176 @@ out:
+       return ret;
+ }
++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);
++}
++
+ /**
+- * __splice_from_pipe - splice data from a pipe to given actor
++ * 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 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.
++
++ *    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.
+  */
+-ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, struct splice_desc *sd,
+-                         splice_actor *actor)
++int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_desc *sd,
++                        splice_actor *actor)
+ {
+-      int ret, do_wakeup, err;
+-
+-      ret = 0;
+-      do_wakeup = 0;
++      int ret;
+-      for (;;) {
+-              if (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;
+-
+-                      err = actor(pipe, buf, sd);
+-                      if (err <= 0) {
+-                              if (!ret && err != -ENODATA)
+-                                      ret = err;
++      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;
++
++              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;
+-                              break;
+-                      }
++              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;
++              }
+-                      ret += err;
+-                      buf->offset += err;
+-                      buf->len -= err;
+-
+-                      sd->len -= err;
+-                      sd->pos += err;
+-                      sd->total_len -= err;
+-                      if (sd->len)
+-                              continue;
+-
+-                      if (!buf->len) {
+-                              buf->ops = NULL;
+-                              ops->release(pipe, buf);
+-                              pipe->curbuf = (pipe->curbuf + 1) & (PIPE_BUFFERS - 1);
+-                              pipe->nrbufs--;
+-                              if (pipe->inode)
+-                                      do_wakeup = 1;
+-                      }
++              if (!sd->total_len)
++                      return 0;
++      }
+-                      if (!sd->total_len)
+-                              break;
+-              }
++      return 1;
++}
++EXPORT_SYMBOL(splice_from_pipe_feed);
+-              if (pipe->nrbufs)
+-                      continue;
++/**
++ * 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)
+-                      break;
+-              if (!pipe->waiting_writers) {
+-                      if (ret)
+-                              break;
+-              }
++                      return 0;
+-              if (sd->flags & SPLICE_F_NONBLOCK) {
+-                      if (!ret)
+-                              ret = -EAGAIN;
+-                      break;
+-              }
++              if (!pipe->waiting_writers && sd->num_spliced)
++                      return 0;
+-              if (signal_pending(current)) {
+-                      if (!ret)
+-                              ret = -ERESTARTSYS;
+-                      break;
+-              }
++              if (sd->flags & SPLICE_F_NONBLOCK)
++                      return -EAGAIN;
+-              if (do_wakeup) {
+-                      smp_mb();
+-                      if (waitqueue_active(&pipe->wait))
+-                              wake_up_interruptible_sync(&pipe->wait);
+-                      kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
+-                      do_wakeup = 0;
++              if (signal_pending(current))
++                      return -ERESTARTSYS;
++
++              if (sd->need_wakeup) {
++                      wakeup_pipe_writers(pipe);
++                      sd->need_wakeup = false;
+               }
+               pipe_wait(pipe);
+       }
+-      if (do_wakeup) {
+-              smp_mb();
+-              if (waitqueue_active(&pipe->wait))
+-                      wake_up_interruptible(&pipe->wait);
+-              kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
+-      }
++      return 1;
++}
++EXPORT_SYMBOL(splice_from_pipe_next);
+-      return ret;
++/**
++ * splice_from_pipe_begin - start splicing from pipe
++ * @pipe:     pipe to splice from
++ *
++ * 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);
++
++/**
++ * __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
++ *
++ * 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 __splice_from_pipe(struct pipe_inode_info *pipe, struct splice_desc *sd,
++                         splice_actor *actor)
++{
++      int ret;
++
++      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);
+--- a/include/linux/splice.h
++++ b/include/linux/splice.h
+@@ -36,6 +36,8 @@ struct splice_desc {
+               void *data;             /* cookie */
+       } u;
+       loff_t pos;                     /* file position */
++      size_t num_spliced;             /* number of bytes already spliced */
++      bool need_wakeup;               /* need to wake up writer */
+ };
+ struct partial_page {
+@@ -66,6 +68,14 @@ extern ssize_t splice_from_pipe(struct p
+                               splice_actor *);
+ extern ssize_t __splice_from_pipe(struct pipe_inode_info *,
+                                 struct splice_desc *, splice_actor *);
++extern int splice_from_pipe_feed(struct pipe_inode_info *, struct splice_desc *,
++                               splice_actor *);
++extern int splice_from_pipe_next(struct pipe_inode_info *,
++                               struct splice_desc *);
++extern void splice_from_pipe_begin(struct splice_desc *);
++extern void splice_from_pipe_end(struct pipe_inode_info *,
++                               struct splice_desc *);
++
+ extern ssize_t splice_to_pipe(struct pipe_inode_info *,
+                             struct splice_pipe_desc *);
+ extern ssize_t splice_direct_to_actor(struct file *, struct splice_desc *,