lkml.org 
[lkml]   [2007]   [Jun]   [12]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 17/18] ext2 xip: replace sendfile with splice
    Date
    From: Carsten Otte <cotte@de.ibm.com>

    This patch removes xip_file_sendfile, and replaces it with
    xip_file_splice_read. It passes the ltp testcase on a ext2 filesystem
    that is mounted -o xip,rw.
    I am not exactly happy with this patch, because it introduces waaaay too
    much code duplication from fs/splice.c. I want to dig into making
    generic_file_splice_read able to deal with xip files aswell. That would
    allow to do the same for splice write.
    If I cannot find a nicer solution, I'd vote for removing sendfile from
    filemap_xip without replacement: a xip filesystem is intended to be used
    for executables and libraries. This content is very rarely subject to
    splice.

    Signed-off-by: Carsten Otte <cotte@de.ibm.com>
    Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
    ---
    fs/ext2/file.c | 2 +-
    include/linux/fs.h | 4 +-
    mm/filemap_xip.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++++----
    3 files changed, 192 insertions(+), 20 deletions(-)

    diff --git a/fs/ext2/file.c b/fs/ext2/file.c
    index 072a190..5f4a17c 100644
    --- a/fs/ext2/file.c
    +++ b/fs/ext2/file.c
    @@ -70,7 +70,7 @@ const struct file_operations ext2_xip_file_operations = {
    .open = generic_file_open,
    .release = ext2_release_file,
    .fsync = ext2_sync_file,
    - .sendfile = xip_file_sendfile,
    + .splice_read = xip_file_splice_read,
    };
    #endif

    diff --git a/include/linux/fs.h b/include/linux/fs.h
    index 61ebf32..a5bf63e 100644
    --- a/include/linux/fs.h
    +++ b/include/linux/fs.h
    @@ -1764,13 +1764,11 @@ extern int nonseekable_open(struct inode * inode, struct file * filp);
    #ifdef CONFIG_FS_XIP
    extern ssize_t xip_file_read(struct file *filp, char __user *buf, size_t len,
    loff_t *ppos);
    -extern ssize_t xip_file_sendfile(struct file *in_file, loff_t *ppos,
    - size_t count, read_actor_t actor,
    - void *target);
    extern int xip_file_mmap(struct file * file, struct vm_area_struct * vma);
    extern ssize_t xip_file_write(struct file *filp, const char __user *buf,
    size_t len, loff_t *ppos);
    extern int xip_truncate_page(struct address_space *mapping, loff_t from);
    +extern ssize_t xip_file_splice_read(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
    #else
    static inline int xip_truncate_page(struct address_space *mapping, loff_t from)
    {
    diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c
    index fa360e5..f1f3134 100644
    --- a/mm/filemap_xip.c
    +++ b/mm/filemap_xip.c
    @@ -14,6 +14,7 @@
    #include <linux/uio.h>
    #include <linux/rmap.h>
    #include <linux/sched.h>
    +#include <linux/splice.h>
    #include <asm/tlbflush.h>
    #include "filemap.h"

    @@ -92,7 +93,11 @@ do_xip_mapping_read(struct address_space *mapping,
    if (unlikely(IS_ERR(page))) {
    if (PTR_ERR(page) == -ENODATA) {
    /* sparse */
    - page = ZERO_PAGE(0);
    + page = xip_sparse_page();
    + if (!page) {
    + desc->error = -ENOMEM;
    + goto out;
    + }
    } else {
    desc->error = PTR_ERR(page);
    goto out;
    @@ -159,27 +164,196 @@ xip_file_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
    }
    EXPORT_SYMBOL_GPL(xip_file_read);

    -ssize_t
    -xip_file_sendfile(struct file *in_file, loff_t *ppos,
    - size_t count, read_actor_t actor, void *target)
    +static int xip_pipe_buf_steal(struct pipe_inode_info *pipe,
    + struct pipe_buffer *buf)
    {
    - read_descriptor_t desc;
    + return 1;
    +}

    - if (!count)
    +static void xip_pipe_buf_release(struct pipe_inode_info *pipe,
    + struct pipe_buffer *buf)
    +{
    + page_cache_release(buf->page);
    +}
    +
    +static const struct pipe_buf_operations xip_pipe_buf_ops = {
    + .can_merge = 0,
    + .pin = generic_pipe_buf_pin,
    + .get = generic_pipe_buf_get,
    + .map = generic_pipe_buf_map,
    + .unmap = generic_pipe_buf_unmap,
    + .release = xip_pipe_buf_release,
    + .steal = xip_pipe_buf_steal,
    +};
    +
    +static int
    +__xip_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 loff, nr_pages;
    + struct page *pages[PIPE_BUFFERS];
    + struct partial_page partial[PIPE_BUFFERS];
    + struct page *page;
    + pgoff_t index, end_index;
    + loff_t isize;
    + size_t total_len;
    + int error, page_nr;
    + struct splice_pipe_desc spd = {
    + .pages = pages,
    + .partial = partial,
    + .flags = flags,
    + .ops = &xip_pipe_buf_ops,
    + };
    +
    + index = *ppos >> PAGE_CACHE_SHIFT;
    + loff = *ppos & ~PAGE_CACHE_MASK;
    + nr_pages = (len + loff + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
    +
    + if (nr_pages > PIPE_BUFFERS)
    + nr_pages = PIPE_BUFFERS;
    +
    + error = 0;
    + total_len = 0;
    +
    + spd.nr_pages = 0;
    + while (spd.nr_pages < nr_pages) {
    + page = mapping->a_ops->get_xip_page(mapping,
    + index*(PAGE_SIZE/512), 0);
    + if (!page) {
    + error = -EIO;
    + break;
    + }
    + if (unlikely(IS_ERR(page))) {
    + if (PTR_ERR(page) == -ENODATA) {
    + /* sparse */
    + page = xip_sparse_page();
    + if (!page) {
    + error = -ENOMEM;
    + break;
    + }
    + } else {
    + error = PTR_ERR(page);
    + break;
    + }
    + }
    + page_cache_get(page);
    + pages[spd.nr_pages++] = page;
    + index++;
    + }
    +
    + /*
    + * Now loop over the map and fill in the partial map
    + */
    + 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 (!len)
    + break;
    +
    + /*
    + * this_len is the max we'll use from this page
    + */
    + this_len = min_t(unsigned long, len, PAGE_CACHE_SIZE - loff);
    + page = pages[page_nr];
    + 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;
    +
    + /*
    + * force quit after adding this page
    + */
    + this_len = min(this_len, plen - loff);
    + len = this_len;
    + }
    +
    + partial[page_nr].offset = loff;
    + partial[page_nr].len = this_len;
    + len -= this_len;
    + loff = 0;
    + spd.nr_pages++;
    + index++;
    + }
    +
    + if (spd.nr_pages)
    + return splice_to_pipe(pipe, &spd);
    +
    + return error;
    +}
    +
    +/**
    + * xip_file_splice_read - splice data from file to a pipe
    + * @in: file to splice from
    + * @pipe: pipe to splice to
    + * @len: number of bytes to splice
    + * @flags: splice modifier flags
    + *
    + * derived from generic_file_splice_read in fs/splice_read.c
    + */
    +ssize_t xip_file_splice_read(struct file *in, loff_t *ppos,
    + struct pipe_inode_info *pipe, size_t len,
    + unsigned int flags)
    +{
    + ssize_t spliced;
    + int ret;
    + loff_t isize, left;
    +
    + BUG_ON(!in->f_mapping->a_ops->get_xip_page);
    +
    + isize = i_size_read(in->f_mapping->host);
    + if (unlikely(*ppos >= isize))
    return 0;

    - desc.written = 0;
    - desc.count = count;
    - desc.arg.data = target;
    - desc.error = 0;
    + left = isize - *ppos;
    + if (unlikely(left < len))
    + len = left;

    - do_xip_mapping_read(in_file->f_mapping, &in_file->f_ra, in_file,
    - ppos, &desc, actor);
    - if (desc.written)
    - return desc.written;
    - return desc.error;
    + ret = 0;
    + spliced = 0;
    + while (len) {
    + ret = __xip_file_splice_read(in, ppos, pipe, len, flags);
    +
    + if (ret < 0)
    + break;
    + else if (!ret) {
    + if (spliced)
    + break;
    + if (flags & SPLICE_F_NONBLOCK) {
    + ret = -EAGAIN;
    + break;
    + }
    + }
    +
    + *ppos += ret;
    + len -= ret;
    + spliced += ret;
    + }
    +
    + if (spliced)
    + return spliced;
    +
    + return ret;
    }
    -EXPORT_SYMBOL_GPL(xip_file_sendfile);
    +EXPORT_SYMBOL(xip_file_splice_read);

    /*
    * __xip_unmap is invoked from xip_unmap and
    --
    1.5.2.1.174.gcd03
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2007-06-12 09:09    [W:0.034 / U:215.504 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site