]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/splice.c
[PATCH] vmsplice: allow user to pass in gift pages
[linux-2.6-omap-h63xx.git] / fs / splice.c
index d7538d83c36729f6e81d0c5eab04babe9a98c825..b150493b6fc33defb17dac4f3af8b28379a33d8d 100644 (file)
@@ -141,7 +141,10 @@ static struct pipe_buf_operations page_cache_pipe_buf_ops = {
 static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe,
                                    struct pipe_buffer *buf)
 {
-       return 1;
+       if (!(buf->flags & PIPE_BUF_FLAG_GIFT))
+               return 1;
+
+       return 0;
 }
 
 static struct pipe_buf_operations user_page_pipe_buf_ops = {
@@ -186,6 +189,9 @@ static ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
                        buf->offset = spd->partial[page_nr].offset;
                        buf->len = spd->partial[page_nr].len;
                        buf->ops = spd->ops;
+                       if (spd->flags & SPLICE_F_GIFT)
+                               buf->flags |= PIPE_BUF_FLAG_GIFT;
+
                        pipe->nrbufs++;
                        page_nr++;
                        ret += buf->len;
@@ -301,6 +307,12 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
                 */
                page = find_get_page(mapping, index);
                if (!page) {
+                       /*
+                        * Make sure the read-ahead engine is notified
+                        * about this failure.
+                        */
+                       handle_ra_miss(mapping, &in->f_ra, index);
+
                        /*
                         * page didn't exist, allocate one.
                         */
@@ -634,13 +646,13 @@ find_page:
                /*
                 * Careful, ->map() uses KM_USER0!
                 */
-               char *src = buf->ops->map(info, buf);
+               char *src = buf->ops->map(info, buf, 1);
                char *dst = kmap_atomic(page, KM_USER1);
 
                memcpy(dst + offset, src + buf->offset, this_len);
                flush_dcache_page(page);
                kunmap_atomic(dst, KM_USER1);
-               buf->ops->unmap(info, buf);
+               buf->ops->unmap(info, buf, src);
        }
 
        ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len);
@@ -1067,7 +1079,7 @@ static long do_splice(struct file *in, loff_t __user *off_in,
  */
 static int get_iovec_page_array(const struct iovec __user *iov,
                                unsigned int nr_vecs, struct page **pages,
-                               struct partial_page *partial)
+                               struct partial_page *partial, int aligned)
 {
        int buffers = 0, error = 0;
 
@@ -1107,6 +1119,15 @@ static int get_iovec_page_array(const struct iovec __user *iov,
                 * 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;
@@ -1200,7 +1221,8 @@ static long do_vmsplice(struct file *file, const struct iovec __user *iov,
        else if (unlikely(!nr_segs))
                return 0;
 
-       spd.nr_pages = get_iovec_page_array(iov, nr_segs, pages, partial);
+       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;
 
@@ -1308,6 +1330,12 @@ static int link_pipe(struct pipe_inode_info *ipipe,
                                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;