splice: add block device specific splice buffered write method
authorJens Axboe <jens.axboe@oracle.com>
Wed, 19 May 2010 19:01:35 +0000 (21:01 +0200)
committerJens Axboe <axboe@nehalem.(none)>
Thu, 20 May 2010 08:43:34 +0000 (10:43 +0200)
Block device write procedure is different from regular file:
 - Actual write performed without i_mutex.
 - It has no metadata, so generic_osync_inode(O_SYNCMETEDATA) can not livelock.
 - We do not have to worry about S_ISUID/S_ISGID bits.

Modified by Jens to better conform with the splice helpers and to apply
on top of the splice branch.

Signed-off-by: Dmitri Monakhov <dmonakhov@openvz.org>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
fs/block_dev.c
fs/splice.c
include/linux/fs.h

index 798513a5d528a1f53bebf6f456ad523c734b2b65..45960313fabc91b4d2c8e51166e7c076e5a495d9 100644 (file)
@@ -1557,7 +1557,7 @@ new_bio:
  * Splice to file opened with O_DIRECT. Bypass caching completely and
  * just go direct-to-bio
  */
-static ssize_t __block_splice_write(struct pipe_inode_info *pipe,
+static ssize_t __block_splice_direct_write(struct pipe_inode_info *pipe,
                                    struct file *out, loff_t *ppos, size_t len,
                                    unsigned int flags)
 {
@@ -1614,9 +1614,10 @@ static ssize_t block_splice_write(struct pipe_inode_info *pipe,
        ssize_t ret;
 
        if (out->f_flags & O_DIRECT)
-               ret = __block_splice_write(pipe, out, ppos, len, flags);
+               ret = __block_splice_direct_write(pipe, out, ppos, len, flags);
        else
-               ret = generic_file_splice_write(pipe, out, ppos, len, flags);
+               ret = generic_file_splice_write_file_nolock(pipe, out, ppos,
+                                                               len, flags);
 
        return ret;
 }
index 05a7b52ad5220fc609a12992d5c8b78f71072981..d712a818cd0830f58e7c56a58666da70e089d39e 100644 (file)
@@ -991,6 +991,61 @@ ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out,
        return ret;
 }
 
+/**
+ * generic_file_splice_write_file_nolock - 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 block device.
+ *    Note: this is like @generic_file_splice_write, except that we
+ *    don't bother locking the output file. Useful for splicing directly
+ *    to a block device.
+ */
+ssize_t generic_file_splice_write_file_nolock(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;
+
+       mutex_lock(&pipe->inode->i_mutex);
+       ret = __splice_from_pipe(pipe, &sd, pipe_to_file);
+       mutex_unlock(&pipe->inode->i_mutex);
+
+       if (ret > 0) {
+               unsigned long nr_pages;
+               loff_t pos = *ppos;
+
+               *ppos += ret;
+               nr_pages = (ret + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+
+               if (unlikely((out->f_flags & O_SYNC) || IS_SYNC(inode))) {
+                       int er;
+
+                       er = filemap_write_and_wait_range(mapping, pos,
+                                                         pos + ret - 1);
+                       if (er)
+                               ret = er;
+               }
+               balance_dirty_pages_ratelimited_nr(mapping, nr_pages);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(generic_file_splice_write_file_nolock);
+
 /**
  * generic_file_splice_write - splice data from a pipe to a file
  * @pipe:      pipe info
index 44f35aea2f1fe8ea178aaa9428d8826033ca034c..589810438e0b601ef387802cb81de32ef424b24b 100644 (file)
@@ -2221,6 +2221,8 @@ extern ssize_t default_file_splice_read(struct file *, loff_t *,
                struct pipe_inode_info *, size_t, unsigned int);
 extern ssize_t generic_file_splice_write(struct pipe_inode_info *,
                struct file *, loff_t *, size_t, unsigned int);
+extern ssize_t generic_file_splice_write_file_nolock(struct pipe_inode_info *,
+               struct file *, loff_t *, size_t, unsigned int);
 extern ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe,
                struct file *out, loff_t *, size_t len, unsigned int flags);
 extern long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,