Commit 01370f06 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'splice-2.6.23' of git://git.kernel.dk/data/git/linux-2.6-block

* 'splice-2.6.23' of git://git.kernel.dk/data/git/linux-2.6-block:
  pipe: add documentation and comments
  pipe: change the ->pin() operation to ->confirm()
  Remove remnants of sendfile()
  xip sendfile removal
  splice: completely document external interface with kerneldoc
  sendfile: remove bad_sendfile() from bad_file_ops
  shmem: convert to using splice instead of sendfile()
  relay: use splice_to_pipe() instead of open-coding the pipe loop
  pipe: allow passing around of ops private pointer
  splice: divorce the splice structure/function definitions from the pipe header
  splice: relay support
  sendfile: convert nfsd to splice_direct_to_actor()
  sendfile: convert nfs to using splice_read()
  loop: convert to using splice_direct_to_actor() instead of sendfile()
  splice: add void cookie to the actor data
  sendfile: kill generic_file_sendfile()
  sendfile: remove .sendfile from filesystems that use generic_file_sendfile()
  sys_sendfile: switch to using ->splice_read, if available
  vmsplice: add vmsplice-to-user support
  splice: abstract out actor data
parents 5cbc39a7 0845718d
...@@ -643,4 +643,15 @@ X!Idrivers/video/console/fonts.c ...@@ -643,4 +643,15 @@ X!Idrivers/video/console/fonts.c
!Edrivers/spi/spi.c !Edrivers/spi/spi.c
</chapter> </chapter>
<chapter id="splice">
<title>splice API</title>
<para>)
splice is a method for moving blocks of data around inside the
kernel, without continually transferring it between the kernel
and user space.
</para>
!Iinclude/linux/splice.h
!Ffs/splice.c
</chapter>
</book> </book>
...@@ -74,6 +74,7 @@ ...@@ -74,6 +74,7 @@
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/splice.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -401,50 +402,73 @@ struct lo_read_data { ...@@ -401,50 +402,73 @@ struct lo_read_data {
}; };
static int static int
lo_read_actor(read_descriptor_t *desc, struct page *page, lo_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
unsigned long offset, unsigned long size) struct splice_desc *sd)
{ {
unsigned long count = desc->count; struct lo_read_data *p = sd->u.data;
struct lo_read_data *p = desc->arg.data;
struct loop_device *lo = p->lo; struct loop_device *lo = p->lo;
struct page *page = buf->page;
sector_t IV; sector_t IV;
size_t size;
int ret;
IV = ((sector_t) page->index << (PAGE_CACHE_SHIFT - 9))+(offset >> 9); ret = buf->ops->confirm(pipe, buf);
if (unlikely(ret))
return ret;
if (size > count) IV = ((sector_t) page->index << (PAGE_CACHE_SHIFT - 9)) +
size = count; (buf->offset >> 9);
size = sd->len;
if (size > p->bsize)
size = p->bsize;
if (lo_do_transfer(lo, READ, page, offset, p->page, p->offset, size, IV)) { if (lo_do_transfer(lo, READ, page, buf->offset, p->page, p->offset, size, IV)) {
size = 0;
printk(KERN_ERR "loop: transfer error block %ld\n", printk(KERN_ERR "loop: transfer error block %ld\n",
page->index); page->index);
desc->error = -EINVAL; size = -EINVAL;
} }
flush_dcache_page(p->page); flush_dcache_page(p->page);
desc->count = count - size; if (size > 0)
desc->written += size; p->offset += size;
p->offset += size;
return size; return size;
} }
static int
lo_direct_splice_actor(struct pipe_inode_info *pipe, struct splice_desc *sd)
{
return __splice_from_pipe(pipe, sd, lo_splice_actor);
}
static int static int
do_lo_receive(struct loop_device *lo, do_lo_receive(struct loop_device *lo,
struct bio_vec *bvec, int bsize, loff_t pos) struct bio_vec *bvec, int bsize, loff_t pos)
{ {
struct lo_read_data cookie; struct lo_read_data cookie;
struct splice_desc sd;
struct file *file; struct file *file;
int retval; long retval;
cookie.lo = lo; cookie.lo = lo;
cookie.page = bvec->bv_page; cookie.page = bvec->bv_page;
cookie.offset = bvec->bv_offset; cookie.offset = bvec->bv_offset;
cookie.bsize = bsize; cookie.bsize = bsize;
sd.len = 0;
sd.total_len = bvec->bv_len;
sd.flags = 0;
sd.pos = pos;
sd.u.data = &cookie;
file = lo->lo_backing_file; file = lo->lo_backing_file;
retval = file->f_op->sendfile(file, &pos, bvec->bv_len, retval = splice_direct_to_actor(file, &sd, lo_direct_splice_actor);
lo_read_actor, &cookie);
return (retval < 0)? retval: 0; if (retval < 0)
return retval;
return 0;
} }
static int static int
...@@ -679,8 +703,8 @@ static int loop_change_fd(struct loop_device *lo, struct file *lo_file, ...@@ -679,8 +703,8 @@ static int loop_change_fd(struct loop_device *lo, struct file *lo_file,
if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode)) if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))
goto out_putf; goto out_putf;
/* new backing store needs to support loop (eg sendfile) */ /* new backing store needs to support loop (eg splice_read) */
if (!inode->i_fop->sendfile) if (!inode->i_fop->splice_read)
goto out_putf; goto out_putf;
/* size of the new backing store needs to be the same */ /* size of the new backing store needs to be the same */
...@@ -760,7 +784,7 @@ static int loop_set_fd(struct loop_device *lo, struct file *lo_file, ...@@ -760,7 +784,7 @@ static int loop_set_fd(struct loop_device *lo, struct file *lo_file,
* If we can't read - sorry. If we only can't write - well, * If we can't read - sorry. If we only can't write - well,
* it's going to be read-only. * it's going to be read-only.
*/ */
if (!file->f_op->sendfile) if (!file->f_op->splice_read)
goto out_putf; goto out_putf;
if (aops->prepare_write && aops->commit_write) if (aops->prepare_write && aops->commit_write)
lo_flags |= LO_FLAGS_USE_AOPS; lo_flags |= LO_FLAGS_USE_AOPS;
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
#include <linux/crash_dump.h> #include <linux/crash_dump.h>
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/bootmem.h> #include <linux/bootmem.h>
#include <linux/pipe_fs_i.h> #include <linux/splice.h>
#include <linux/pfn.h> #include <linux/pfn.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
......
...@@ -33,7 +33,7 @@ const struct file_operations adfs_file_operations = { ...@@ -33,7 +33,7 @@ const struct file_operations adfs_file_operations = {
.fsync = file_fsync, .fsync = file_fsync,
.write = do_sync_write, .write = do_sync_write,
.aio_write = generic_file_aio_write, .aio_write = generic_file_aio_write,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
}; };
const struct inode_operations adfs_file_inode_operations = { const struct inode_operations adfs_file_inode_operations = {
......
...@@ -35,7 +35,7 @@ const struct file_operations affs_file_operations = { ...@@ -35,7 +35,7 @@ const struct file_operations affs_file_operations = {
.open = affs_file_open, .open = affs_file_open,
.release = affs_file_release, .release = affs_file_release,
.fsync = file_fsync, .fsync = file_fsync,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
}; };
const struct inode_operations affs_file_inode_operations = { const struct inode_operations affs_file_inode_operations = {
......
...@@ -32,7 +32,7 @@ const struct file_operations afs_file_operations = { ...@@ -32,7 +32,7 @@ const struct file_operations afs_file_operations = {
.aio_read = generic_file_aio_read, .aio_read = generic_file_aio_read,
.aio_write = afs_file_write, .aio_write = afs_file_write,
.mmap = generic_file_readonly_mmap, .mmap = generic_file_readonly_mmap,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
.fsync = afs_fsync, .fsync = afs_fsync,
}; };
......
...@@ -114,12 +114,6 @@ static int bad_file_lock(struct file *file, int cmd, struct file_lock *fl) ...@@ -114,12 +114,6 @@ static int bad_file_lock(struct file *file, int cmd, struct file_lock *fl)
return -EIO; return -EIO;
} }
static ssize_t bad_file_sendfile(struct file *in_file, loff_t *ppos,
size_t count, read_actor_t actor, void *target)
{
return -EIO;
}
static ssize_t bad_file_sendpage(struct file *file, struct page *page, static ssize_t bad_file_sendpage(struct file *file, struct page *page,
int off, size_t len, loff_t *pos, int more) int off, size_t len, loff_t *pos, int more)
{ {
...@@ -182,7 +176,6 @@ static const struct file_operations bad_file_ops = ...@@ -182,7 +176,6 @@ static const struct file_operations bad_file_ops =
.aio_fsync = bad_file_aio_fsync, .aio_fsync = bad_file_aio_fsync,
.fasync = bad_file_fasync, .fasync = bad_file_fasync,
.lock = bad_file_lock, .lock = bad_file_lock,
.sendfile = bad_file_sendfile,
.sendpage = bad_file_sendpage, .sendpage = bad_file_sendpage,
.get_unmapped_area = bad_file_get_unmapped_area, .get_unmapped_area = bad_file_get_unmapped_area,
.check_flags = bad_file_check_flags, .check_flags = bad_file_check_flags,
......
...@@ -24,7 +24,7 @@ const struct file_operations bfs_file_operations = { ...@@ -24,7 +24,7 @@ const struct file_operations bfs_file_operations = {
.write = do_sync_write, .write = do_sync_write,
.aio_write = generic_file_aio_write, .aio_write = generic_file_aio_write,
.mmap = generic_file_mmap, .mmap = generic_file_mmap,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
}; };
static int bfs_move_block(unsigned long from, unsigned long to, struct super_block *sb) static int bfs_move_block(unsigned long from, unsigned long to, struct super_block *sb)
......
...@@ -1346,7 +1346,6 @@ const struct file_operations def_blk_fops = { ...@@ -1346,7 +1346,6 @@ const struct file_operations def_blk_fops = {
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
.compat_ioctl = compat_blkdev_ioctl, .compat_ioctl = compat_blkdev_ioctl,
#endif #endif
.sendfile = generic_file_sendfile,
.splice_read = generic_file_splice_read, .splice_read = generic_file_splice_read,
.splice_write = generic_file_splice_write, .splice_write = generic_file_splice_write,
}; };
......
...@@ -616,7 +616,7 @@ const struct file_operations cifs_file_ops = { ...@@ -616,7 +616,7 @@ const struct file_operations cifs_file_ops = {
.fsync = cifs_fsync, .fsync = cifs_fsync,
.flush = cifs_flush, .flush = cifs_flush,
.mmap = cifs_file_mmap, .mmap = cifs_file_mmap,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
.llseek = cifs_llseek, .llseek = cifs_llseek,
#ifdef CONFIG_CIFS_POSIX #ifdef CONFIG_CIFS_POSIX
.ioctl = cifs_ioctl, .ioctl = cifs_ioctl,
...@@ -637,7 +637,7 @@ const struct file_operations cifs_file_direct_ops = { ...@@ -637,7 +637,7 @@ const struct file_operations cifs_file_direct_ops = {
.lock = cifs_lock, .lock = cifs_lock,
.fsync = cifs_fsync, .fsync = cifs_fsync,
.flush = cifs_flush, .flush = cifs_flush,
.sendfile = generic_file_sendfile, /* BB removeme BB */ .splice_read = generic_file_splice_read,
#ifdef CONFIG_CIFS_POSIX #ifdef CONFIG_CIFS_POSIX
.ioctl = cifs_ioctl, .ioctl = cifs_ioctl,
#endif /* CONFIG_CIFS_POSIX */ #endif /* CONFIG_CIFS_POSIX */
...@@ -656,7 +656,7 @@ const struct file_operations cifs_file_nobrl_ops = { ...@@ -656,7 +656,7 @@ const struct file_operations cifs_file_nobrl_ops = {
.fsync = cifs_fsync, .fsync = cifs_fsync,
.flush = cifs_flush, .flush = cifs_flush,
.mmap = cifs_file_mmap, .mmap = cifs_file_mmap,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
.llseek = cifs_llseek, .llseek = cifs_llseek,
#ifdef CONFIG_CIFS_POSIX #ifdef CONFIG_CIFS_POSIX
.ioctl = cifs_ioctl, .ioctl = cifs_ioctl,
...@@ -676,7 +676,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = { ...@@ -676,7 +676,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = {
.release = cifs_close, .release = cifs_close,
.fsync = cifs_fsync, .fsync = cifs_fsync,
.flush = cifs_flush, .flush = cifs_flush,
.sendfile = generic_file_sendfile, /* BB removeme BB */ .splice_read = generic_file_splice_read,
#ifdef CONFIG_CIFS_POSIX #ifdef CONFIG_CIFS_POSIX
.ioctl = cifs_ioctl, .ioctl = cifs_ioctl,
#endif /* CONFIG_CIFS_POSIX */ #endif /* CONFIG_CIFS_POSIX */
......
...@@ -47,8 +47,9 @@ coda_file_read(struct file *coda_file, char __user *buf, size_t count, loff_t *p ...@@ -47,8 +47,9 @@ coda_file_read(struct file *coda_file, char __user *buf, size_t count, loff_t *p
} }
static ssize_t static ssize_t
coda_file_sendfile(struct file *coda_file, loff_t *ppos, size_t count, coda_file_splice_read(struct file *coda_file, loff_t *ppos,
read_actor_t actor, void *target) struct pipe_inode_info *pipe, size_t count,
unsigned int flags)
{ {
struct coda_file_info *cfi; struct coda_file_info *cfi;
struct file *host_file; struct file *host_file;
...@@ -57,10 +58,10 @@ coda_file_sendfile(struct file *coda_file, loff_t *ppos, size_t count, ...@@ -57,10 +58,10 @@ coda_file_sendfile(struct file *coda_file, loff_t *ppos, size_t count,
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
host_file = cfi->cfi_container; host_file = cfi->cfi_container;
if (!host_file->f_op || !host_file->f_op->sendfile) if (!host_file->f_op || !host_file->f_op->splice_read)
return -EINVAL; return -EINVAL;
return host_file->f_op->sendfile(host_file, ppos, count, actor, target); return host_file->f_op->splice_read(host_file, ppos, pipe, count,flags);
} }
static ssize_t static ssize_t
...@@ -295,6 +296,6 @@ const struct file_operations coda_file_operations = { ...@@ -295,6 +296,6 @@ const struct file_operations coda_file_operations = {
.flush = coda_flush, .flush = coda_flush,
.release = coda_release, .release = coda_release,
.fsync = coda_fsync, .fsync = coda_fsync,
.sendfile = coda_file_sendfile, .splice_read = coda_file_splice_read,
}; };
...@@ -338,16 +338,17 @@ static int ecryptfs_fasync(int fd, struct file *file, int flag) ...@@ -338,16 +338,17 @@ static int ecryptfs_fasync(int fd, struct file *file, int flag)
return rc; return rc;
} }
static ssize_t ecryptfs_sendfile(struct file *file, loff_t * ppos, static ssize_t ecryptfs_splice_read(struct file *file, loff_t * ppos,
size_t count, read_actor_t actor, void *target) struct pipe_inode_info *pipe, size_t count,
unsigned int flags)
{ {
struct file *lower_file = NULL; struct file *lower_file = NULL;
int rc = -EINVAL; int rc = -EINVAL;
lower_file = ecryptfs_file_to_lower(file); lower_file = ecryptfs_file_to_lower(file);
if (lower_file->f_op && lower_file->f_op->sendfile) if (lower_file->f_op && lower_file->f_op->splice_read)
rc = lower_file->f_op->sendfile(lower_file, ppos, count, rc = lower_file->f_op->splice_read(lower_file, ppos, pipe,
actor, target); count, flags);
return rc; return rc;
} }
...@@ -364,7 +365,7 @@ const struct file_operations ecryptfs_dir_fops = { ...@@ -364,7 +365,7 @@ const struct file_operations ecryptfs_dir_fops = {
.release = ecryptfs_release, .release = ecryptfs_release,
.fsync = ecryptfs_fsync, .fsync = ecryptfs_fsync,
.fasync = ecryptfs_fasync, .fasync = ecryptfs_fasync,
.sendfile = ecryptfs_sendfile, .splice_read = ecryptfs_splice_read,
}; };
const struct file_operations ecryptfs_main_fops = { const struct file_operations ecryptfs_main_fops = {
...@@ -381,7 +382,7 @@ const struct file_operations ecryptfs_main_fops = { ...@@ -381,7 +382,7 @@ const struct file_operations ecryptfs_main_fops = {
.release = ecryptfs_release, .release = ecryptfs_release,
.fsync = ecryptfs_fsync, .fsync = ecryptfs_fsync,
.fasync = ecryptfs_fasync, .fasync = ecryptfs_fasync,
.sendfile = ecryptfs_sendfile, .splice_read = ecryptfs_splice_read,
}; };
static int static int
......
...@@ -53,7 +53,6 @@ const struct file_operations ext2_file_operations = { ...@@ -53,7 +53,6 @@ const struct file_operations ext2_file_operations = {
.open = generic_file_open, .open = generic_file_open,
.release = ext2_release_file, .release = ext2_release_file,
.fsync = ext2_sync_file, .fsync = ext2_sync_file,
.sendfile = generic_file_sendfile,
.splice_read = generic_file_splice_read, .splice_read = generic_file_splice_read,
.splice_write = generic_file_splice_write, .splice_write = generic_file_splice_write,
}; };
...@@ -71,7 +70,6 @@ const struct file_operations ext2_xip_file_operations = { ...@@ -71,7 +70,6 @@ const struct file_operations ext2_xip_file_operations = {
.open = generic_file_open, .open = generic_file_open,
.release = ext2_release_file, .release = ext2_release_file,
.fsync = ext2_sync_file, .fsync = ext2_sync_file,
.sendfile = xip_file_sendfile,
}; };
#endif #endif
......
...@@ -120,7 +120,6 @@ const struct file_operations ext3_file_operations = { ...@@ -120,7 +120,6 @@ const struct file_operations ext3_file_operations = {
.open = generic_file_open, .open = generic_file_open,
.release = ext3_release_file, .release = ext3_release_file,
.fsync = ext3_sync_file, .fsync = ext3_sync_file,
.sendfile = generic_file_sendfile,
.splice_read = generic_file_splice_read, .splice_read = generic_file_splice_read,
.splice_write = generic_file_splice_write, .splice_write = generic_file_splice_write,
}; };
......
...@@ -120,7 +120,6 @@ const struct file_operations ext4_file_operations = { ...@@ -120,7 +120,6 @@ const struct file_operations ext4_file_operations = {
.open = generic_file_open, .open = generic_file_open,
.release = ext4_release_file, .release = ext4_release_file,
.fsync = ext4_sync_file, .fsync = ext4_sync_file,
.sendfile = generic_file_sendfile,
.splice_read = generic_file_splice_read, .splice_read = generic_file_splice_read,
.splice_write = generic_file_splice_write, .splice_write = generic_file_splice_write,
}; };
......
...@@ -134,7 +134,7 @@ const struct file_operations fat_file_operations = { ...@@ -134,7 +134,7 @@ const struct file_operations fat_file_operations = {
.release = fat_file_release, .release = fat_file_release,
.ioctl = fat_generic_ioctl, .ioctl = fat_generic_ioctl,
.fsync = file_fsync, .fsync = file_fsync,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
}; };
static int fat_cont_expand(struct inode *inode, loff_t size) static int fat_cont_expand(struct inode *inode, loff_t size)
......
...@@ -802,7 +802,7 @@ static const struct file_operations fuse_file_operations = { ...@@ -802,7 +802,7 @@ static const struct file_operations fuse_file_operations = {
.release = fuse_release, .release = fuse_release,
.fsync = fuse_fsync, .fsync = fuse_fsync,
.lock = fuse_file_lock, .lock = fuse_file_lock,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
}; };
static const struct file_operations fuse_direct_io_file_operations = { static const struct file_operations fuse_direct_io_file_operations = {
...@@ -814,7 +814,7 @@ static const struct file_operations fuse_direct_io_file_operations = { ...@@ -814,7 +814,7 @@ static const struct file_operations fuse_direct_io_file_operations = {
.release = fuse_release, .release = fuse_release,
.fsync = fuse_fsync, .fsync = fuse_fsync,
.lock = fuse_file_lock, .lock = fuse_file_lock,
/* no mmap and sendfile */ /* no mmap and splice_read */
}; };
static const struct address_space_operations fuse_file_aops = { static const struct address_space_operations fuse_file_aops = {
......
...@@ -635,7 +635,6 @@ const struct file_operations gfs2_file_fops = { ...@@ -635,7 +635,6 @@ const struct file_operations gfs2_file_fops = {
.release = gfs2_close, .release = gfs2_close,
.fsync = gfs2_fsync, .fsync = gfs2_fsync,
.lock = gfs2_lock, .lock = gfs2_lock,
.sendfile = generic_file_sendfile,
.flock = gfs2_flock, .flock = gfs2_flock,
.splice_read = generic_file_splice_read, .splice_read = generic_file_splice_read,
.splice_write = generic_file_splice_write, .splice_write = generic_file_splice_write,
......
...@@ -607,7 +607,7 @@ static const struct file_operations hfs_file_operations = { ...@@ -607,7 +607,7 @@ static const struct file_operations hfs_file_operations = {
.write = do_sync_write, .write = do_sync_write,
.aio_write = generic_file_aio_write, .aio_write = generic_file_aio_write,
.mmap = generic_file_mmap, .mmap = generic_file_mmap,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
.fsync = file_fsync, .fsync = file_fsync,
.open = hfs_file_open, .open = hfs_file_open,
.release = hfs_file_release, .release = hfs_file_release,
......
...@@ -288,7 +288,7 @@ static const struct file_operations hfsplus_file_operations = { ...@@ -288,7 +288,7 @@ static const struct file_operations hfsplus_file_operations = {
.write = do_sync_write, .write = do_sync_write,
.aio_write = generic_file_aio_write, .aio_write = generic_file_aio_write,
.mmap = generic_file_mmap, .mmap = generic_file_mmap,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
.fsync = file_fsync, .fsync = file_fsync,
.open = hfsplus_file_open, .open = hfsplus_file_open,
.release = hfsplus_file_release, .release = hfsplus_file_release,
......
...@@ -390,7 +390,7 @@ int hostfs_fsync(struct file *file, struct dentry *dentry, int datasync) ...@@ -390,7 +390,7 @@ int hostfs_fsync(struct file *file, struct dentry *dentry, int datasync)
static const struct file_operations hostfs_file_fops = { static const struct file_operations hostfs_file_fops = {
.llseek = generic_file_llseek, .llseek = generic_file_llseek,
.read = do_sync_read, .read = do_sync_read,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
.aio_read = generic_file_aio_read, .aio_read = generic_file_aio_read,
.aio_write = generic_file_aio_write, .aio_write = generic_file_aio_write,
.write = do_sync_write, .write = do_sync_write,
......
...@@ -129,7 +129,7 @@ const struct file_operations hpfs_file_ops = ...@@ -129,7 +129,7 @@ const struct file_operations hpfs_file_ops =
.mmap = generic_file_mmap, .mmap = generic_file_mmap,
.release = hpfs_file_release, .release = hpfs_file_release,
.fsync = hpfs_file_fsync, .fsync = hpfs_file_fsync,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
}; };
const struct inode_operations hpfs_file_iops = const struct inode_operations hpfs_file_iops =
......
...@@ -47,7 +47,7 @@ const struct file_operations jffs2_file_operations = ...@@ -47,7 +47,7 @@ const struct file_operations jffs2_file_operations =
.ioctl = jffs2_ioctl, .ioctl = jffs2_ioctl,
.mmap = generic_file_readonly_mmap, .mmap = generic_file_readonly_mmap,
.fsync = jffs2_fsync, .fsync = jffs2_fsync,
.sendfile = generic_file_sendfile .splice_read = generic_file_splice_read,
}; };
/* jffs2_file_inode_operations */ /* jffs2_file_inode_operations */
......
...@@ -108,7 +108,6 @@ const struct file_operations jfs_file_operations = { ...@@ -108,7 +108,6 @@ const struct file_operations jfs_file_operations = {
.aio_read = generic_file_aio_read, .aio_read = generic_file_aio_read,
.aio_write = generic_file_aio_write, .aio_write = generic_file_aio_write,
.mmap = generic_file_mmap, .mmap = generic_file_mmap,
.sendfile = generic_file_sendfile,
.splice_read = generic_file_splice_read, .splice_read = generic_file_splice_read,
.splice_write = generic_file_splice_write, .splice_write = generic_file_splice_write,
.fsync = jfs_fsync, .fsync = jfs_fsync,
......
...@@ -23,7 +23,7 @@ const struct file_operations minix_file_operations = { ...@@ -23,7 +23,7 @@ const struct file_operations minix_file_operations = {
.aio_write = generic_file_aio_write, .aio_write = generic_file_aio_write,
.mmap = generic_file_mmap, .mmap = generic_file_mmap,
.fsync = minix_sync_file, .fsync = minix_sync_file,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
}; };
const struct inode_operations minix_file_inode_operations = { const struct inode_operations minix_file_inode_operations = {
......
...@@ -41,7 +41,9 @@ static int nfs_file_open(struct inode *, struct file *); ...@@ -41,7 +41,9 @@ static int nfs_file_open(struct inode *, struct file *);
static int nfs_file_release(struct inode *, struct file *); static int nfs_file_release(struct inode *, struct file *);
static loff_t nfs_file_llseek(struct file *file, loff_t offset, int origin); static loff_t nfs_file_llseek(struct file *file, loff_t offset, int origin);
static int nfs_file_mmap(struct file *, struct vm_area_struct *); static int nfs_file_mmap(struct file *, struct vm_area_struct *);
static ssize_t nfs_file_sendfile(struct file *, loff_t *, size_t, read_actor_t, void *); static ssize_t nfs_file_splice_read(struct file *filp, loff_t *ppos,
struct pipe_inode_info *pipe,
size_t count, unsigned int flags);
static ssize_t nfs_file_read(struct kiocb *, const struct iovec *iov, static ssize_t nfs_file_read(struct kiocb *, const struct iovec *iov,
unsigned long nr_segs, loff_t pos); unsigned long nr_segs, loff_t pos);
static ssize_t nfs_file_write(struct kiocb *, const struct iovec *iov, static ssize_t nfs_file_write(struct kiocb *, const struct iovec *iov,
...@@ -65,7 +67,7 @@ const struct file_operations nfs_file_operations = { ...@@ -65,7 +67,7 @@ const struct file_operations nfs_file_operations = {
.fsync = nfs_fsync, .fsync = nfs_fsync,
.lock = nfs_lock, .lock = nfs_lock,
.flock = nfs_flock, .flock = nfs_flock,
.sendfile = nfs_file_sendfile, .splice_read = nfs_file_splice_read,
.check_flags = nfs_check_flags, .check_flags = nfs_check_flags,
}; };
...@@ -224,20 +226,21 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov, ...@@ -224,20 +226,21 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov,
} }
static ssize_t static ssize_t
nfs_file_sendfile(struct file *filp, loff_t *ppos, size_t count, nfs_file_splice_read(struct file *filp, loff_t *ppos,
read_actor_t actor, void *target) struct pipe_inode_info *pipe, size_t count,
unsigned int flags)
{ {
struct dentry *dentry = filp->f_path.dentry; struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
ssize_t res; ssize_t res;
dfprintk(VFS, "nfs: sendfile(%s/%s, %lu@%Lu)\n", dfprintk(VFS, "nfs: splice_read(%s/%s, %lu@%Lu)\n",
dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_parent->d_name.name, dentry->d_name.name,
(unsigned long) count, (unsigned long long) *ppos); (unsigned long) count, (unsigned long long) *ppos);
res = nfs_revalidate_mapping(inode, filp->f_mapping); res = nfs_revalidate_mapping(inode, filp->f_mapping);
if (!res) if (!res)
res = generic_file_sendfile(filp, ppos, count, actor, target); res = generic_file_splice_read(filp, ppos, pipe, count, flags);
return res; return res;
} }
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
#include <linux/file.h> #include <linux/file.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/major.h> #include <linux/major.h>
#include <linux/ext2_fs.h> #include <linux/splice.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/stat.h> #include <linux/stat.h>
#include <linux/fcntl.h> #include <linux/fcntl.h>
...@@ -801,26 +801,32 @@ nfsd_get_raparms(dev_t dev, ino_t ino) ...@@ -801,26 +801,32 @@ nfsd_get_raparms(dev_t dev, ino_t ino)
} }
/* /*
* Grab and keep cached pages assosiated with a file in the svc_rqst * Grab and keep cached pages associated with a file in the svc_rqst
* so that they can be passed to the netowork sendmsg/sendpage routines * so that they can be passed to the network sendmsg/sendpage routines
* directrly. They will be released after the sending has completed. * directly. They will be released after the sending has completed.
*/ */
static int static int
nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset , unsigned long size) nfsd_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
struct splice_desc *sd)
{ {
unsigned long count = desc->count; struct svc_rqst *rqstp = sd->u.data;
struct svc_rqst *rqstp = desc->arg.data;
struct page **pp = rqstp->rq_respages + rqstp->rq_resused; struct page **pp = rqstp->rq_respages + rqstp->rq_resused;
struct page *page = buf->page;
size_t size;
int ret;
ret = buf->ops->confirm(pipe, buf);
if (unlikely(ret))
return ret;
if (size > count) size = sd->len;
size = count;
if (rqstp->rq_res.page_len == 0) { if (rqstp->rq_res.page_len == 0) {
get_page(page); get_page(page);
put_page(*pp); put_page(*pp);
*pp = page; *pp = page;
rqstp->rq_resused++; rqstp->rq_resused++;
rqstp->rq_res.page_base = offset; rqstp->rq_res.page_base = buf->offset;
rqstp->rq_res.page_len = size; rqstp->rq_res.page_len = size;
} else if (page != pp[-1]) { } else if (page != pp[-1]) {
get_page(page); get_page(page);
...@@ -832,11 +838,15 @@ nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset ...@@ -832,11 +838,15 @@ nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset
} else } else
rqstp->rq_res.page_len += size; rqstp->rq_res.page_len += size;
desc->count = count - size;
desc->written += size;
return size; return size;
} }
static int nfsd_direct_splice_actor(struct pipe_inode_info *pipe,
struct splice_desc *sd)
{
return __splice_from_pipe(pipe, sd, nfsd_splice_actor);
}
static __be32 static __be32
nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
loff_t offset, struct kvec *vec, int vlen, unsigned long *count) loff_t offset, struct kvec *vec, int vlen, unsigned long *count)
...@@ -861,10 +871,15 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, ...@@ -861,10 +871,15 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
if (ra && ra->p_set) if (ra && ra->p_set)
file->f_ra = ra->p_ra; file->f_ra = ra->p_ra;
if (file->f_op->sendfile && rqstp->rq_sendfile_ok) { if (file->f_op->splice_read && rqstp->rq_splice_ok) {
rqstp->rq_resused = 1; struct splice_desc sd = {
host_err = file->f_op->sendfile(file, &offset, *count, .len = 0,
nfsd_read_actor, rqstp); .total_len = *count,
.pos = offset,
.u.data = rqstp,
};
host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor);
} else { } else {
oldfs = get_fs(); oldfs = get_fs();
set_fs(KERNEL_DS); set_fs(KERNEL_DS);
......
...@@ -2276,7 +2276,7 @@ const struct file_operations ntfs_file_ops = { ...@@ -2276,7 +2276,7 @@ const struct file_operations ntfs_file_ops = {
mounted filesystem. */ mounted filesystem. */
.mmap = generic_file_mmap, /* Mmap file. */ .mmap = generic_file_mmap, /* Mmap file. */
.open = ntfs_file_open, /* Open file. */ .open = ntfs_file_open, /* Open file. */
.sendfile = generic_file_sendfile, /* Zero-copy data send with .splice_read = generic_file_splice_read /* Zero-copy data send with
the data source being on the data source being on
the ntfs partition. We do the ntfs partition. We do
not need to care about the not need to care about the
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/pipe_fs_i.h> #include <linux/splice.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/writeback.h> #include <linux/writeback.h>
...@@ -1583,7 +1583,7 @@ static int ocfs2_splice_write_actor(struct pipe_inode_info *pipe, ...@@ -1583,7 +1583,7 @@ static int ocfs2_splice_write_actor(struct pipe_inode_info *pipe,
ssize_t copied = 0; ssize_t copied = 0;
struct ocfs2_splice_write_priv sp; struct ocfs2_splice_write_priv sp;
ret = buf->ops->pin(pipe, buf); ret = buf->ops->confirm(pipe, buf);
if (ret) if (ret)
goto out; goto out;
...@@ -1604,7 +1604,7 @@ static int ocfs2_splice_write_actor(struct pipe_inode_info *pipe, ...@@ -1604,7 +1604,7 @@ static int ocfs2_splice_write_actor(struct pipe_inode_info *pipe,
* might enter ocfs2_buffered_write_cluster() more * might enter ocfs2_buffered_write_cluster() more
* than once, so keep track of our progress here. * than once, so keep track of our progress here.
*/ */
copied = ocfs2_buffered_write_cluster(sd->file, copied = ocfs2_buffered_write_cluster(sd->u.file,
(loff_t)sd->pos + total, (loff_t)sd->pos + total,
count, count,
ocfs2_map_and_write_splice_data, ocfs2_map_and_write_splice_data,
...@@ -1636,9 +1636,14 @@ static ssize_t __ocfs2_file_splice_write(struct pipe_inode_info *pipe, ...@@ -1636,9 +1636,14 @@ static ssize_t __ocfs2_file_splice_write(struct pipe_inode_info *pipe,
int ret, err; int ret, err;
struct address_space *mapping = out->f_mapping; struct address_space *mapping = out->f_mapping;
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
struct splice_desc sd = {
ret = __splice_from_pipe(pipe, out, ppos, len, flags, .total_len = len,
ocfs2_splice_write_actor); .flags = flags,
.pos = *ppos,
.u.file = out,
};
ret = __splice_from_pipe(pipe, &sd, ocfs2_splice_write_actor);
if (ret > 0) { if (ret > 0) {
*ppos += ret; *ppos += ret;
...@@ -1817,7 +1822,6 @@ const struct inode_operations ocfs2_special_file_iops = { ...@@ -1817,7 +1822,6 @@ const struct inode_operations ocfs2_special_file_iops = {
const struct file_operations ocfs2_fops = { const struct file_operations ocfs2_fops = {
.read = do_sync_read, .read = do_sync_read,
.write = do_sync_write, .write = do_sync_write,
.sendfile = generic_file_sendfile,
.mmap = ocfs2_mmap, .mmap = ocfs2_mmap,
.fsync = ocfs2_sync_file, .fsync = ocfs2_sync_file,
.release = ocfs2_file_release, .release = ocfs2_file_release,
......
...@@ -164,6 +164,20 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe, ...@@ -164,6 +164,20 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe,
page_cache_release(page); page_cache_release(page);
} }
/**
* generic_pipe_buf_map - virtually map a pipe buffer
* @pipe: the pipe that the buffer belongs to
* @buf: the buffer that should be mapped
* @atomic: whether to use an atomic map
*
* Description:
* This function returns a kernel virtual address mapping for the
* passed in @pipe_buffer. If @atomic is set, an atomic map is provided
* and the caller has to be careful not to fault before calling
* the unmap function.
*
* Note that this function occupies KM_USER0 if @atomic != 0.
*/
void *generic_pipe_buf_map(struct pipe_inode_info *pipe, void *generic_pipe_buf_map(struct pipe_inode_info *pipe,
struct pipe_buffer *buf, int atomic) struct pipe_buffer *buf, int atomic)
{ {
...@@ -175,6 +189,15 @@ void *generic_pipe_buf_map(struct pipe_inode_info *pipe, ...@@ -175,6 +189,15 @@ void *generic_pipe_buf_map(struct pipe_inode_info *pipe,
return kmap(buf->page); return kmap(buf->page);
} }
/**
* generic_pipe_buf_unmap - unmap a previously mapped pipe buffer
* @pipe: the pipe that the buffer belongs to
* @buf: the buffer that should be unmapped
* @map_data: the data that the mapping function returned
*
* Description:
* This function undoes the mapping that ->map() provided.
*/
void generic_pipe_buf_unmap(struct pipe_inode_info *pipe, void generic_pipe_buf_unmap(struct pipe_inode_info *pipe,
struct pipe_buffer *buf, void *map_data) struct pipe_buffer *buf, void *map_data)
{ {
...@@ -185,11 +208,28 @@ void generic_pipe_buf_unmap(struct pipe_inode_info *pipe, ...@@ -185,11 +208,28 @@ void generic_pipe_buf_unmap(struct pipe_inode_info *pipe,
kunmap(buf->page); kunmap(buf->page);
} }
/**
* generic_pipe_buf_steal - attempt to take ownership of a @pipe_buffer
* @pipe: the pipe that the buffer belongs to
* @buf: the buffer to attempt to steal
*
* Description:
* This function attempts to steal the @struct page attached to
* @buf. If successful, this function returns 0 and returns with
* the page locked. The caller may then reuse the page for whatever
* he wishes, the typical use is insertion into a different file
* page cache.
*/
int generic_pipe_buf_steal(struct pipe_inode_info *pipe, int generic_pipe_buf_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf) struct pipe_buffer *buf)
{ {
struct page *page = buf->page; struct page *page = buf->page;
/*
* A reference of one is golden, that means that the owner of this
* page is the only one holding a reference to it. lock the page
* and return OK.
*/
if (page_count(page) == 1) { if (page_count(page) == 1) {
lock_page(page); lock_page(page);
return 0; return 0;
...@@ -198,12 +238,32 @@ int generic_pipe_buf_steal(struct pipe_inode_info *pipe, ...@@ -198,12 +238,32 @@ int generic_pipe_buf_steal(struct pipe_inode_info *pipe,
return 1; return 1;
} }
void generic_pipe_buf_get(struct pipe_inode_info *info, struct pipe_buffer *buf) /**
* generic_pipe_buf_get - get a reference to a @struct pipe_buffer
* @pipe: the pipe that the buffer belongs to
* @buf: the buffer to get a reference to
*
* Description:
* This function grabs an extra reference to @buf. It's used in
* in the tee() system call, when we duplicate the buffers in one
* pipe into another.
*/
void generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf)
{ {
page_cache_get(buf->page); page_cache_get(buf->page);
} }
int generic_pipe_buf_pin(struct pipe_inode_info *info, struct pipe_buffer *buf) /**
* generic_pipe_buf_confirm - verify contents of the pipe buffer
* @pipe: the pipe that the buffer belongs to
* @buf: the buffer to confirm
*
* Description:
* This function does nothing, because the generic pipe code uses
* pages that are always good when inserted into the pipe.
*/
int generic_pipe_buf_confirm(struct pipe_inode_info *info,
struct pipe_buffer *buf)
{ {
return 0; return 0;
} }
...@@ -212,7 +272,7 @@ static const struct pipe_buf_operations anon_pipe_buf_ops = { ...@@ -212,7 +272,7 @@ static const struct pipe_buf_operations anon_pipe_buf_ops = {
.can_merge = 1, .can_merge = 1,
.map = generic_pipe_buf_map, .map = generic_pipe_buf_map,
.unmap = generic_pipe_buf_unmap, .unmap = generic_pipe_buf_unmap,
.pin = generic_pipe_buf_pin, .confirm = generic_pipe_buf_confirm,
.release = anon_pipe_buf_release, .release = anon_pipe_buf_release,
.steal = generic_pipe_buf_steal, .steal = generic_pipe_buf_steal,
.get = generic_pipe_buf_get, .get = generic_pipe_buf_get,
...@@ -252,7 +312,7 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov, ...@@ -252,7 +312,7 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov,
if (chars > total_len) if (chars > total_len)
chars = total_len; chars = total_len;
error = ops->pin(pipe, buf); error = ops->confirm(pipe, buf);
if (error) { if (error) {
if (!ret) if (!ret)
error = ret; error = ret;
...@@ -373,7 +433,7 @@ pipe_write(struct kiocb *iocb, const struct iovec *_iov, ...@@ -373,7 +433,7 @@ pipe_write(struct kiocb *iocb, const struct iovec *_iov,
int error, atomic = 1; int error, atomic = 1;
void *addr; void *addr;
error = ops->pin(pipe, buf); error = ops->confirm(pipe, buf);
if (error) if (error)
goto out; goto out;
......
...@@ -25,7 +25,7 @@ const struct file_operations qnx4_file_operations = ...@@ -25,7 +25,7 @@ const struct file_operations qnx4_file_operations =
.read = do_sync_read, .read = do_sync_read,
.aio_read = generic_file_aio_read, .aio_read = generic_file_aio_read,
.mmap = generic_file_mmap, .mmap = generic_file_mmap,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
#ifdef CONFIG_QNX4FS_RW #ifdef CONFIG_QNX4FS_RW
.write = do_sync_write, .write = do_sync_write,
.aio_write = generic_file_aio_write, .aio_write = generic_file_aio_write,
......
...@@ -41,7 +41,7 @@ const struct file_operations ramfs_file_operations = { ...@@ -41,7 +41,7 @@ const struct file_operations ramfs_file_operations = {
.aio_write = generic_file_aio_write, .aio_write = generic_file_aio_write,
.mmap = generic_file_mmap, .mmap = generic_file_mmap,
.fsync = simple_sync_file, .fsync = simple_sync_file,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
.llseek = generic_file_llseek, .llseek = generic_file_llseek,
}; };
......
...@@ -42,7 +42,7 @@ const struct file_operations ramfs_file_operations = { ...@@ -42,7 +42,7 @@ const struct file_operations ramfs_file_operations = {
.write = do_sync_write, .write = do_sync_write,
.aio_write = generic_file_aio_write, .aio_write = generic_file_aio_write,
.fsync = simple_sync_file, .fsync = simple_sync_file,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
.llseek = generic_file_llseek, .llseek = generic_file_llseek,
}; };
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/splice.h>
#include "read_write.h" #include "read_write.h"
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -25,7 +26,7 @@ const struct file_operations generic_ro_fops = { ...@@ -25,7 +26,7 @@ const struct file_operations generic_ro_fops = {
.read = do_sync_read, .read = do_sync_read,
.aio_read = generic_file_aio_read, .aio_read = generic_file_aio_read,
.mmap = generic_file_readonly_mmap, .mmap = generic_file_readonly_mmap,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
}; };
EXPORT_SYMBOL(generic_ro_fops); EXPORT_SYMBOL(generic_ro_fops);
...@@ -708,7 +709,7 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, ...@@ -708,7 +709,7 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
struct inode * in_inode, * out_inode; struct inode * in_inode, * out_inode;
loff_t pos; loff_t pos;
ssize_t retval; ssize_t retval;
int fput_needed_in, fput_needed_out; int fput_needed_in, fput_needed_out, fl;
/* /*
* Get input file, and verify that it is ok.. * Get input file, and verify that it is ok..
...@@ -723,7 +724,7 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, ...@@ -723,7 +724,7 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
in_inode = in_file->f_path.dentry->d_inode; in_inode = in_file->f_path.dentry->d_inode;
if (!in_inode) if (!in_inode)
goto fput_in; goto fput_in;
if (!in_file->f_op || !in_file->f_op->sendfile) if (!in_file->f_op || !in_file->f_op->splice_read)
goto fput_in; goto fput_in;
retval = -ESPIPE; retval = -ESPIPE;
if (!ppos) if (!ppos)
...@@ -776,7 +777,18 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, ...@@ -776,7 +777,18 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
count = max - pos; count = max - pos;
} }
retval = in_file->f_op->sendfile(in_file, ppos, count, file_send_actor, out_file); fl = 0;
#if 0
/*
* We need to debate whether we can enable this or not. The
* man page documents EAGAIN return for the output at least,
* and the application is arguably buggy if it doesn't expect
* EAGAIN on a non-blocking file descriptor.
*/
if (in_file->f_flags & O_NONBLOCK)
fl = SPLICE_F_NONBLOCK;
#endif
retval = do_splice_direct(in_file, ppos, out_file, count, fl);
if (retval > 0) { if (retval > 0) {
add_rchar(current, retval); add_rchar(current, retval);
......
...@@ -1531,7 +1531,6 @@ const struct file_operations reiserfs_file_operations = { ...@@ -1531,7 +1531,6 @@ const struct file_operations reiserfs_file_operations = {
.open = generic_file_open, .open = generic_file_open,
.release = reiserfs_file_release, .release = reiserfs_file_release,
.fsync = reiserfs_sync_file, .fsync = reiserfs_sync_file,
.sendfile = generic_file_sendfile,
.aio_read = generic_file_aio_read, .aio_read = generic_file_aio_read,
.aio_write = generic_file_aio_write, .aio_write = generic_file_aio_write,
.splice_read = generic_file_splice_read, .splice_read = generic_file_splice_read,
......
...@@ -262,8 +262,9 @@ smb_file_mmap(struct file * file, struct vm_area_struct * vma) ...@@ -262,8 +262,9 @@ smb_file_mmap(struct file * file, struct vm_area_struct * vma)
} }
static ssize_t static ssize_t
smb_file_sendfile(struct file *file, loff_t *ppos, smb_file_splice_read(struct file *file, loff_t *ppos,
size_t count, read_actor_t actor, void *target) struct pipe_inode_info *pipe, size_t count,
unsigned int flags)
{ {
struct dentry *dentry = file->f_path.dentry; struct dentry *dentry = file->f_path.dentry;
ssize_t status; ssize_t status;
...@@ -277,7 +278,7 @@ smb_file_sendfile(struct file *file, loff_t *ppos, ...@@ -277,7 +278,7 @@ smb_file_sendfile(struct file *file, loff_t *ppos,
DENTRY_PATH(dentry), status); DENTRY_PATH(dentry), status);
goto out; goto out;
} }
status = generic_file_sendfile(file, ppos, count, actor, target); status = generic_file_splice_read(file, ppos, pipe, count, flags);
out: out:
return status; return status;
} }
...@@ -416,7 +417,7 @@ const struct file_operations smb_file_operations = ...@@ -416,7 +417,7 @@ const struct file_operations smb_file_operations =
.open = smb_file_open, .open = smb_file_open,
.release = smb_file_release, .release = smb_file_release,
.fsync = smb_fsync, .fsync = smb_fsync,
.sendfile = smb_file_sendfile, .splice_read = smb_file_splice_read,
}; };
const struct inode_operations smb_file_inode_operations = const struct inode_operations smb_file_inode_operations =
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/file.h> #include <linux/file.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/pipe_fs_i.h> #include <linux/splice.h>
#include <linux/mm_inline.h> #include <linux/mm_inline.h>
#include <linux/swap.h> #include <linux/swap.h>
#include <linux/writeback.h> #include <linux/writeback.h>
...@@ -29,22 +29,6 @@ ...@@ -29,22 +29,6 @@
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/uio.h> #include <linux/uio.h>
struct partial_page {
unsigned int offset;
unsigned int len;
};
/*
* Passed to splice_to_pipe
*/
struct splice_pipe_desc {
struct page **pages; /* page map */
struct partial_page *partial; /* pages[] may not be contig */
int nr_pages; /* number of pages in map */
unsigned int flags; /* splice flags */
const struct pipe_buf_operations *ops;/* ops associated with output pipe */
};
/* /*
* Attempt to steal a page from a pipe buffer. This should perhaps go into * Attempt to steal a page from a pipe buffer. This should perhaps go into
* a vm helper function, it's already simplified quite a bit by the * a vm helper function, it's already simplified quite a bit by the
...@@ -101,8 +85,12 @@ static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe, ...@@ -101,8 +85,12 @@ static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe,
buf->flags &= ~PIPE_BUF_FLAG_LRU; buf->flags &= ~PIPE_BUF_FLAG_LRU;
} }
static int page_cache_pipe_buf_pin(struct pipe_inode_info *pipe, /*
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; struct page *page = buf->page;
int err; int err;
...@@ -143,7 +131,7 @@ static const struct pipe_buf_operations page_cache_pipe_buf_ops = { ...@@ -143,7 +131,7 @@ static const struct pipe_buf_operations page_cache_pipe_buf_ops = {
.can_merge = 0, .can_merge = 0,
.map = generic_pipe_buf_map, .map = generic_pipe_buf_map,
.unmap = generic_pipe_buf_unmap, .unmap = generic_pipe_buf_unmap,
.pin = page_cache_pipe_buf_pin, .confirm = page_cache_pipe_buf_confirm,
.release = page_cache_pipe_buf_release, .release = page_cache_pipe_buf_release,
.steal = page_cache_pipe_buf_steal, .steal = page_cache_pipe_buf_steal,
.get = generic_pipe_buf_get, .get = generic_pipe_buf_get,
...@@ -163,18 +151,25 @@ static const struct pipe_buf_operations user_page_pipe_buf_ops = { ...@@ -163,18 +151,25 @@ static const struct pipe_buf_operations user_page_pipe_buf_ops = {
.can_merge = 0, .can_merge = 0,
.map = generic_pipe_buf_map, .map = generic_pipe_buf_map,
.unmap = generic_pipe_buf_unmap, .unmap = generic_pipe_buf_unmap,
.pin = generic_pipe_buf_pin, .confirm = generic_pipe_buf_confirm,
.release = page_cache_pipe_buf_release, .release = page_cache_pipe_buf_release,
.steal = user_page_pipe_buf_steal, .steal = user_page_pipe_buf_steal,
.get = generic_pipe_buf_get, .get = generic_pipe_buf_get,
}; };
/* /**
* Pipe output worker. This sets up our pipe format with the page cache * splice_to_pipe - fill passed data into a pipe
* pipe buffer operations. Otherwise very similar to the regular pipe_writev(). * @pipe: pipe to fill
* @spd: data to fill
*
* Description:
* @spd contains a map of pages and len/offset tupples, a long with
* the struct pipe_buf_operations associated with these pages. This
* function will link that data to the pipe.
*
*/ */
static ssize_t splice_to_pipe(struct pipe_inode_info *pipe, ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
struct splice_pipe_desc *spd) struct splice_pipe_desc *spd)
{ {
unsigned int spd_pages = spd->nr_pages; unsigned int spd_pages = spd->nr_pages;
int ret, do_wakeup, page_nr; int ret, do_wakeup, page_nr;
...@@ -201,6 +196,7 @@ static ssize_t splice_to_pipe(struct pipe_inode_info *pipe, ...@@ -201,6 +196,7 @@ static ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
buf->page = spd->pages[page_nr]; buf->page = spd->pages[page_nr];
buf->offset = spd->partial[page_nr].offset; buf->offset = spd->partial[page_nr].offset;
buf->len = spd->partial[page_nr].len; buf->len = spd->partial[page_nr].len;
buf->private = spd->partial[page_nr].private;
buf->ops = spd->ops; buf->ops = spd->ops;
if (spd->flags & SPLICE_F_GIFT) if (spd->flags & SPLICE_F_GIFT)
buf->flags |= PIPE_BUF_FLAG_GIFT; buf->flags |= PIPE_BUF_FLAG_GIFT;
...@@ -295,11 +291,6 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, ...@@ -295,11 +291,6 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
*/ */
page_cache_readahead(mapping, &in->f_ra, in, index, nr_pages); page_cache_readahead(mapping, &in->f_ra, in, index, nr_pages);
/*
* Now fill in the holes:
*/
error = 0;
/* /*
* Lookup the (hopefully) full range of pages we need. * Lookup the (hopefully) full range of pages we need.
*/ */
...@@ -307,8 +298,9 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, ...@@ -307,8 +298,9 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
/* /*
* If find_get_pages_contig() returned fewer pages than we needed, * If find_get_pages_contig() returned fewer pages than we needed,
* allocate the rest. * allocate the rest and fill in the holes.
*/ */
error = 0;
index += spd.nr_pages; index += spd.nr_pages;
while (spd.nr_pages < nr_pages) { while (spd.nr_pages < nr_pages) {
/* /*
...@@ -470,11 +462,16 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, ...@@ -470,11 +462,16 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
/** /**
* generic_file_splice_read - splice data from file to a pipe * generic_file_splice_read - splice data from file to a pipe
* @in: file to splice from * @in: file to splice from
* @ppos: position in @in
* @pipe: pipe to splice to * @pipe: pipe to splice to
* @len: number of bytes to splice * @len: number of bytes to splice
* @flags: splice modifier flags * @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, loff_t *ppos, ssize_t generic_file_splice_read(struct file *in, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len, struct pipe_inode_info *pipe, size_t len,
...@@ -528,11 +525,11 @@ EXPORT_SYMBOL(generic_file_splice_read); ...@@ -528,11 +525,11 @@ EXPORT_SYMBOL(generic_file_splice_read);
static int pipe_to_sendpage(struct pipe_inode_info *pipe, static int pipe_to_sendpage(struct pipe_inode_info *pipe,
struct pipe_buffer *buf, struct splice_desc *sd) struct pipe_buffer *buf, struct splice_desc *sd)
{ {
struct file *file = sd->file; struct file *file = sd->u.file;
loff_t pos = sd->pos; loff_t pos = sd->pos;
int ret, more; int ret, more;
ret = buf->ops->pin(pipe, buf); ret = buf->ops->confirm(pipe, buf);
if (!ret) { if (!ret) {
more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len; more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len;
...@@ -566,7 +563,7 @@ static int pipe_to_sendpage(struct pipe_inode_info *pipe, ...@@ -566,7 +563,7 @@ static int pipe_to_sendpage(struct pipe_inode_info *pipe,
static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf, static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
struct splice_desc *sd) struct splice_desc *sd)
{ {
struct file *file = sd->file; struct file *file = sd->u.file;
struct address_space *mapping = file->f_mapping; struct address_space *mapping = file->f_mapping;
unsigned int offset, this_len; unsigned int offset, this_len;
struct page *page; struct page *page;
...@@ -576,7 +573,7 @@ static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf, ...@@ -576,7 +573,7 @@ static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
/* /*
* make sure the data in this buffer is uptodate * make sure the data in this buffer is uptodate
*/ */
ret = buf->ops->pin(pipe, buf); ret = buf->ops->confirm(pipe, buf);
if (unlikely(ret)) if (unlikely(ret))
return ret; return ret;
...@@ -663,36 +660,37 @@ static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf, ...@@ -663,36 +660,37 @@ static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
return ret; return ret;
} }
/* /**
* Pipe input worker. Most of this logic works like a regular pipe, the * __splice_from_pipe - splice data from a pipe to given actor
* key here is the 'actor' worker passed in that actually moves the data * @pipe: pipe to splice from
* to the wanted destination. See pipe_to_file/pipe_to_sendpage above. * @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, ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, struct splice_desc *sd,
struct file *out, loff_t *ppos, size_t len, splice_actor *actor)
unsigned int flags, splice_actor *actor)
{ {
int ret, do_wakeup, err; int ret, do_wakeup, err;
struct splice_desc sd;
ret = 0; ret = 0;
do_wakeup = 0; do_wakeup = 0;
sd.total_len = len;
sd.flags = flags;
sd.file = out;
sd.pos = *ppos;
for (;;) { for (;;) {
if (pipe->nrbufs) { if (pipe->nrbufs) {
struct pipe_buffer *buf = pipe->bufs + pipe->curbuf; struct pipe_buffer *buf = pipe->bufs + pipe->curbuf;
const struct pipe_buf_operations *ops = buf->ops; const struct pipe_buf_operations *ops = buf->ops;
sd.len = buf->len; sd->len = buf->len;
if (sd.len > sd.total_len) if (sd->len > sd->total_len)
sd.len = sd.total_len; sd->len = sd->total_len;
err = actor(pipe, buf, &sd); err = actor(pipe, buf, sd);
if (err <= 0) { if (err <= 0) {
if (!ret && err != -ENODATA) if (!ret && err != -ENODATA)
ret = err; ret = err;
...@@ -704,10 +702,10 @@ ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, ...@@ -704,10 +702,10 @@ ssize_t __splice_from_pipe(struct pipe_inode_info *pipe,
buf->offset += err; buf->offset += err;
buf->len -= err; buf->len -= err;
sd.len -= err; sd->len -= err;
sd.pos += err; sd->pos += err;
sd.total_len -= err; sd->total_len -= err;
if (sd.len) if (sd->len)
continue; continue;
if (!buf->len) { if (!buf->len) {
...@@ -719,7 +717,7 @@ ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, ...@@ -719,7 +717,7 @@ ssize_t __splice_from_pipe(struct pipe_inode_info *pipe,
do_wakeup = 1; do_wakeup = 1;
} }
if (!sd.total_len) if (!sd->total_len)
break; break;
} }
...@@ -732,7 +730,7 @@ ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, ...@@ -732,7 +730,7 @@ ssize_t __splice_from_pipe(struct pipe_inode_info *pipe,
break; break;
} }
if (flags & SPLICE_F_NONBLOCK) { if (sd->flags & SPLICE_F_NONBLOCK) {
if (!ret) if (!ret)
ret = -EAGAIN; ret = -EAGAIN;
break; break;
...@@ -766,12 +764,32 @@ ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, ...@@ -766,12 +764,32 @@ ssize_t __splice_from_pipe(struct pipe_inode_info *pipe,
} }
EXPORT_SYMBOL(__splice_from_pipe); 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 input and output inodes,
* otherwise it's identical to __splice_from_pipe().
*
*/
ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out, ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out,
loff_t *ppos, size_t len, unsigned int flags, loff_t *ppos, size_t len, unsigned int flags,
splice_actor *actor) splice_actor *actor)
{ {
ssize_t ret; ssize_t ret;
struct inode *inode = out->f_mapping->host; struct inode *inode = out->f_mapping->host;
struct splice_desc sd = {
.total_len = len,
.flags = flags,
.pos = *ppos,
.u.file = out,
};
/* /*
* The actor worker might be calling ->prepare_write and * The actor worker might be calling ->prepare_write and
...@@ -780,7 +798,7 @@ ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out, ...@@ -780,7 +798,7 @@ ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out,
* pipe->inode, we have to order lock acquiry here. * pipe->inode, we have to order lock acquiry here.
*/ */
inode_double_lock(inode, pipe->inode); inode_double_lock(inode, pipe->inode);
ret = __splice_from_pipe(pipe, out, ppos, len, flags, actor); ret = __splice_from_pipe(pipe, &sd, actor);
inode_double_unlock(inode, pipe->inode); inode_double_unlock(inode, pipe->inode);
return ret; return ret;
...@@ -790,12 +808,14 @@ ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out, ...@@ -790,12 +808,14 @@ ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out,
* generic_file_splice_write_nolock - generic_file_splice_write without mutexes * generic_file_splice_write_nolock - generic_file_splice_write without mutexes
* @pipe: pipe info * @pipe: pipe info
* @out: file to write to * @out: file to write to
* @ppos: position in @out
* @len: number of bytes to splice * @len: number of bytes to splice
* @flags: splice modifier flags * @flags: splice modifier flags
* *
* Will either move or copy pages (determined by @flags options) from * Description:
* the given pipe inode to the given file. The caller is responsible * Will either move or copy pages (determined by @flags options) from
* for acquiring i_mutex on both inodes. * the given pipe inode to the given file. The caller is responsible
* for acquiring i_mutex on both inodes.
* *
*/ */
ssize_t ssize_t
...@@ -804,6 +824,12 @@ generic_file_splice_write_nolock(struct pipe_inode_info *pipe, struct file *out, ...@@ -804,6 +824,12 @@ generic_file_splice_write_nolock(struct pipe_inode_info *pipe, struct file *out,
{ {
struct address_space *mapping = out->f_mapping; struct address_space *mapping = out->f_mapping;
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
struct splice_desc sd = {
.total_len = len,
.flags = flags,
.pos = *ppos,
.u.file = out,
};
ssize_t ret; ssize_t ret;
int err; int err;
...@@ -811,7 +837,7 @@ generic_file_splice_write_nolock(struct pipe_inode_info *pipe, struct file *out, ...@@ -811,7 +837,7 @@ generic_file_splice_write_nolock(struct pipe_inode_info *pipe, struct file *out,
if (unlikely(err)) if (unlikely(err))
return err; return err;
ret = __splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_file); ret = __splice_from_pipe(pipe, &sd, pipe_to_file);
if (ret > 0) { if (ret > 0) {
unsigned long nr_pages; unsigned long nr_pages;
...@@ -841,11 +867,13 @@ EXPORT_SYMBOL(generic_file_splice_write_nolock); ...@@ -841,11 +867,13 @@ EXPORT_SYMBOL(generic_file_splice_write_nolock);
* generic_file_splice_write - splice data from a pipe to a file * generic_file_splice_write - splice data from a pipe to a file
* @pipe: pipe info * @pipe: pipe info
* @out: file to write to * @out: file to write to
* @ppos: position in @out
* @len: number of bytes to splice * @len: number of bytes to splice
* @flags: splice modifier flags * @flags: splice modifier flags
* *
* Will either move or copy pages (determined by @flags options) from * Description:
* the given pipe inode to the given file. * Will either move or copy pages (determined by @flags options) from
* the given pipe inode to the given file.
* *
*/ */
ssize_t ssize_t
...@@ -896,13 +924,15 @@ EXPORT_SYMBOL(generic_file_splice_write); ...@@ -896,13 +924,15 @@ EXPORT_SYMBOL(generic_file_splice_write);
/** /**
* generic_splice_sendpage - splice data from a pipe to a socket * generic_splice_sendpage - splice data from a pipe to a socket
* @inode: pipe inode * @pipe: pipe to splice from
* @out: socket to write to * @out: socket to write to
* @ppos: position in @out
* @len: number of bytes to splice * @len: number of bytes to splice
* @flags: splice modifier flags * @flags: splice modifier flags
* *
* Will send @len bytes from the pipe to a network socket. No data copying * Description:
* is involved. * Will send @len bytes from the pipe to a network socket. No data copying
* is involved.
* *
*/ */
ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, struct file *out, ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, struct file *out,
...@@ -956,14 +986,27 @@ static long do_splice_to(struct file *in, loff_t *ppos, ...@@ -956,14 +986,27 @@ static long do_splice_to(struct file *in, loff_t *ppos,
return in->f_op->splice_read(in, ppos, pipe, len, flags); return in->f_op->splice_read(in, ppos, pipe, len, flags);
} }
long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, /**
size_t len, unsigned int 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 life time 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; struct pipe_inode_info *pipe;
long ret, bytes; long ret, bytes;
loff_t out_off;
umode_t i_mode; umode_t i_mode;
int i; size_t len;
int i, flags;
/* /*
* We require the input being a regular file, as we don't want to * We require the input being a regular file, as we don't want to
...@@ -999,7 +1042,13 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, ...@@ -999,7 +1042,13 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
*/ */
ret = 0; ret = 0;
bytes = 0; bytes = 0;
out_off = 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) { while (len) {
size_t read_len, max_read_len; size_t read_len, max_read_len;
...@@ -1009,19 +1058,19 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, ...@@ -1009,19 +1058,19 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
*/ */
max_read_len = min(len, (size_t)(PIPE_BUFFERS*PAGE_SIZE)); max_read_len = min(len, (size_t)(PIPE_BUFFERS*PAGE_SIZE));
ret = do_splice_to(in, ppos, pipe, max_read_len, flags); ret = do_splice_to(in, &sd->pos, pipe, max_read_len, flags);
if (unlikely(ret < 0)) if (unlikely(ret < 0))
goto out_release; goto out_release;
read_len = ret; read_len = ret;
sd->total_len = read_len;
/* /*
* NOTE: nonblocking mode only applies to the input. We * NOTE: nonblocking mode only applies to the input. We
* must not do the output in nonblocking mode as then we * must not do the output in nonblocking mode as then we
* could get stuck data in the internal pipe: * could get stuck data in the internal pipe:
*/ */
ret = do_splice_from(pipe, out, &out_off, read_len, ret = actor(pipe, sd);
flags & ~SPLICE_F_NONBLOCK);
if (unlikely(ret < 0)) if (unlikely(ret < 0))
goto out_release; goto out_release;
...@@ -1066,6 +1115,48 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, ...@@ -1066,6 +1115,48 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
return bytes; return bytes;
return ret; return ret;
}
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,
};
size_t ret;
ret = splice_direct_to_actor(in, &sd, direct_splice_actor);
*ppos = sd.pos;
return ret;
} }
/* /*
...@@ -1248,28 +1339,131 @@ static int get_iovec_page_array(const struct iovec __user *iov, ...@@ -1248,28 +1339,131 @@ static int get_iovec_page_array(const struct iovec __user *iov,
return error; 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;
out:
if (ret > 0)
sd->u.userptr += ret;
buf->ops->unmap(pipe, buf, src);
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;
if (pipe->inode)
mutex_lock(&pipe->inode->i_mutex);
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;
}
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++;
}
if (pipe->inode)
mutex_unlock(&pipe->inode->i_mutex);
if (!ret)
ret = error;
return ret;
}
/* /*
* vmsplice splices a user address range into a pipe. It can be thought of * 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 * as splice-from-memory, where the regular splice is splice-from-file (or
* to file). In both cases the output is a pipe, naturally. * to file). In both cases the output is a pipe, naturally.
*
* Note that vmsplice only supports 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).
*
* Alas, it isn't here.
*
*/ */
static long do_vmsplice(struct file *file, const struct iovec __user *iov, static long vmsplice_to_pipe(struct file *file, const struct iovec __user *iov,
unsigned long nr_segs, unsigned int flags) unsigned long nr_segs, unsigned int flags)
{ {
struct pipe_inode_info *pipe; struct pipe_inode_info *pipe;
struct page *pages[PIPE_BUFFERS]; struct page *pages[PIPE_BUFFERS];
...@@ -1284,10 +1478,6 @@ static long do_vmsplice(struct file *file, const struct iovec __user *iov, ...@@ -1284,10 +1478,6 @@ static long do_vmsplice(struct file *file, const struct iovec __user *iov,
pipe = pipe_info(file->f_path.dentry->d_inode); pipe = pipe_info(file->f_path.dentry->d_inode);
if (!pipe) if (!pipe)
return -EBADF; return -EBADF;
if (unlikely(nr_segs > UIO_MAXIOV))
return -EINVAL;
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); flags & SPLICE_F_GIFT);
...@@ -1297,6 +1487,22 @@ static long do_vmsplice(struct file *file, const struct iovec __user *iov, ...@@ -1297,6 +1487,22 @@ static long do_vmsplice(struct file *file, const struct iovec __user *iov,
return splice_to_pipe(pipe, &spd); 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().
*
*/
asmlinkage long sys_vmsplice(int fd, const struct iovec __user *iov, asmlinkage long sys_vmsplice(int fd, const struct iovec __user *iov,
unsigned long nr_segs, unsigned int flags) unsigned long nr_segs, unsigned int flags)
{ {
...@@ -1304,11 +1510,18 @@ asmlinkage long sys_vmsplice(int fd, const struct iovec __user *iov, ...@@ -1304,11 +1510,18 @@ asmlinkage long sys_vmsplice(int fd, const struct iovec __user *iov,
long error; long error;
int fput; int fput;
if (unlikely(nr_segs > UIO_MAXIOV))
return -EINVAL;
else if (unlikely(!nr_segs))
return 0;
error = -EBADF; error = -EBADF;
file = fget_light(fd, &fput); file = fget_light(fd, &fput);
if (file) { if (file) {
if (file->f_mode & FMODE_WRITE) if (file->f_mode & FMODE_WRITE)
error = do_vmsplice(file, iov, nr_segs, flags); 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); fput_light(file, fput);
} }
......
...@@ -27,7 +27,7 @@ const struct file_operations sysv_file_operations = { ...@@ -27,7 +27,7 @@ const struct file_operations sysv_file_operations = {
.aio_write = generic_file_aio_write, .aio_write = generic_file_aio_write,
.mmap = generic_file_mmap, .mmap = generic_file_mmap,
.fsync = sysv_sync_file, .fsync = sysv_sync_file,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
}; };
const struct inode_operations sysv_file_inode_operations = { const struct inode_operations sysv_file_inode_operations = {
......
...@@ -261,7 +261,7 @@ const struct file_operations udf_file_operations = { ...@@ -261,7 +261,7 @@ const struct file_operations udf_file_operations = {
.aio_write = udf_file_aio_write, .aio_write = udf_file_aio_write,
.release = udf_release_file, .release = udf_release_file,
.fsync = udf_fsync_file, .fsync = udf_fsync_file,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
}; };
const struct inode_operations udf_file_inode_operations = { const struct inode_operations udf_file_inode_operations = {
......
...@@ -60,5 +60,5 @@ const struct file_operations ufs_file_operations = { ...@@ -60,5 +60,5 @@ const struct file_operations ufs_file_operations = {
.mmap = generic_file_mmap, .mmap = generic_file_mmap,
.open = generic_file_open, .open = generic_file_open,
.fsync = ufs_sync_file, .fsync = ufs_sync_file,
.sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read,
}; };
...@@ -123,30 +123,6 @@ xfs_file_aio_write_invis( ...@@ -123,30 +123,6 @@ xfs_file_aio_write_invis(
return __xfs_file_write(iocb, iov, nr_segs, IO_ISAIO|IO_INVIS, pos); return __xfs_file_write(iocb, iov, nr_segs, IO_ISAIO|IO_INVIS, pos);
} }
STATIC ssize_t
xfs_file_sendfile(
struct file *filp,
loff_t *pos,
size_t count,
read_actor_t actor,
void *target)
{
return bhv_vop_sendfile(vn_from_inode(filp->f_path.dentry->d_inode),
filp, pos, 0, count, actor, target, NULL);
}
STATIC ssize_t
xfs_file_sendfile_invis(
struct file *filp,
loff_t *pos,
size_t count,
read_actor_t actor,
void *target)
{
return bhv_vop_sendfile(vn_from_inode(filp->f_path.dentry->d_inode),
filp, pos, IO_INVIS, count, actor, target, NULL);
}
STATIC ssize_t STATIC ssize_t
xfs_file_splice_read( xfs_file_splice_read(
struct file *infilp, struct file *infilp,
...@@ -452,7 +428,6 @@ const struct file_operations xfs_file_operations = { ...@@ -452,7 +428,6 @@ const struct file_operations xfs_file_operations = {
.write = do_sync_write, .write = do_sync_write,
.aio_read = xfs_file_aio_read, .aio_read = xfs_file_aio_read,
.aio_write = xfs_file_aio_write, .aio_write = xfs_file_aio_write,
.sendfile = xfs_file_sendfile,
.splice_read = xfs_file_splice_read, .splice_read = xfs_file_splice_read,
.splice_write = xfs_file_splice_write, .splice_write = xfs_file_splice_write,
.unlocked_ioctl = xfs_file_ioctl, .unlocked_ioctl = xfs_file_ioctl,
...@@ -475,7 +450,6 @@ const struct file_operations xfs_invis_file_operations = { ...@@ -475,7 +450,6 @@ const struct file_operations xfs_invis_file_operations = {
.write = do_sync_write, .write = do_sync_write,
.aio_read = xfs_file_aio_read_invis, .aio_read = xfs_file_aio_read_invis,
.aio_write = xfs_file_aio_write_invis, .aio_write = xfs_file_aio_write_invis,
.sendfile = xfs_file_sendfile_invis,
.splice_read = xfs_file_splice_read_invis, .splice_read = xfs_file_splice_read_invis,
.splice_write = xfs_file_splice_write_invis, .splice_write = xfs_file_splice_write_invis,
.unlocked_ioctl = xfs_file_ioctl_invis, .unlocked_ioctl = xfs_file_ioctl_invis,
......
...@@ -101,7 +101,6 @@ ...@@ -101,7 +101,6 @@
* Feature macros (disable/enable) * Feature macros (disable/enable)
*/ */
#undef HAVE_REFCACHE /* reference cache not needed for NFS in 2.6 */ #undef HAVE_REFCACHE /* reference cache not needed for NFS in 2.6 */
#define HAVE_SENDFILE /* sendfile(2) exists in 2.6, but not in 2.4 */
#define HAVE_SPLICE /* a splice(2) exists in 2.6, but not in 2.4 */ #define HAVE_SPLICE /* a splice(2) exists in 2.6, but not in 2.4 */
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
#define HAVE_PERCPU_SB /* per cpu superblock counters are a 2.6 feature */ #define HAVE_PERCPU_SB /* per cpu superblock counters are a 2.6 feature */
......
...@@ -286,50 +286,6 @@ xfs_read( ...@@ -286,50 +286,6 @@ xfs_read(
return ret; return ret;
} }
ssize_t
xfs_sendfile(
bhv_desc_t *bdp,
struct file *filp,
loff_t *offset,
int ioflags,
size_t count,
read_actor_t actor,
void *target,
cred_t *credp)
{
xfs_inode_t *ip = XFS_BHVTOI(bdp);
xfs_mount_t *mp = ip->i_mount;
ssize_t ret;
XFS_STATS_INC(xs_read_calls);
if (XFS_FORCED_SHUTDOWN(mp))
return -EIO;
xfs_ilock(ip, XFS_IOLOCK_SHARED);
if (DM_EVENT_ENABLED(BHV_TO_VNODE(bdp)->v_vfsp, ip, DM_EVENT_READ) &&
(!(ioflags & IO_INVIS))) {
bhv_vrwlock_t locktype = VRWLOCK_READ;
int error;
error = XFS_SEND_DATA(mp, DM_EVENT_READ, BHV_TO_VNODE(bdp),
*offset, count,
FILP_DELAY_FLAG(filp), &locktype);
if (error) {
xfs_iunlock(ip, XFS_IOLOCK_SHARED);
return -error;
}
}
xfs_rw_enter_trace(XFS_SENDFILE_ENTER, &ip->i_iocore,
(void *)(unsigned long)target, count, *offset, ioflags);
ret = generic_file_sendfile(filp, offset, count, actor, target);
if (ret > 0)
XFS_STATS_ADD(xs_read_bytes, ret);
xfs_iunlock(ip, XFS_IOLOCK_SHARED);
return ret;
}
ssize_t ssize_t
xfs_splice_read( xfs_splice_read(
bhv_desc_t *bdp, bhv_desc_t *bdp,
......
...@@ -90,9 +90,6 @@ extern ssize_t xfs_read(struct bhv_desc *, struct kiocb *, ...@@ -90,9 +90,6 @@ extern ssize_t xfs_read(struct bhv_desc *, struct kiocb *,
extern ssize_t xfs_write(struct bhv_desc *, struct kiocb *, extern ssize_t xfs_write(struct bhv_desc *, struct kiocb *,
const struct iovec *, unsigned int, const struct iovec *, unsigned int,
loff_t *, int, struct cred *); loff_t *, int, struct cred *);
extern ssize_t xfs_sendfile(struct bhv_desc *, struct file *,
loff_t *, int, size_t, read_actor_t,
void *, struct cred *);
extern ssize_t xfs_splice_read(struct bhv_desc *, struct file *, loff_t *, extern ssize_t xfs_splice_read(struct bhv_desc *, struct file *, loff_t *,
struct pipe_inode_info *, size_t, int, int, struct pipe_inode_info *, size_t, int, int,
struct cred *); struct cred *);
......
...@@ -139,9 +139,6 @@ typedef ssize_t (*vop_read_t)(bhv_desc_t *, struct kiocb *, ...@@ -139,9 +139,6 @@ typedef ssize_t (*vop_read_t)(bhv_desc_t *, struct kiocb *,
typedef ssize_t (*vop_write_t)(bhv_desc_t *, struct kiocb *, typedef ssize_t (*vop_write_t)(bhv_desc_t *, struct kiocb *,
const struct iovec *, unsigned int, const struct iovec *, unsigned int,
loff_t *, int, struct cred *); loff_t *, int, struct cred *);
typedef ssize_t (*vop_sendfile_t)(bhv_desc_t *, struct file *,
loff_t *, int, size_t, read_actor_t,
void *, struct cred *);
typedef ssize_t (*vop_splice_read_t)(bhv_desc_t *, struct file *, loff_t *, typedef ssize_t (*vop_splice_read_t)(bhv_desc_t *, struct file *, loff_t *,
struct pipe_inode_info *, size_t, int, int, struct pipe_inode_info *, size_t, int, int,
struct cred *); struct cred *);
...@@ -206,7 +203,6 @@ typedef struct bhv_vnodeops { ...@@ -206,7 +203,6 @@ typedef struct bhv_vnodeops {
vop_close_t vop_close; vop_close_t vop_close;
vop_read_t vop_read; vop_read_t vop_read;
vop_write_t vop_write; vop_write_t vop_write;
vop_sendfile_t vop_sendfile;
vop_splice_read_t vop_splice_read; vop_splice_read_t vop_splice_read;
vop_splice_write_t vop_splice_write; vop_splice_write_t vop_splice_write;
vop_ioctl_t vop_ioctl; vop_ioctl_t vop_ioctl;
...@@ -254,8 +250,6 @@ typedef struct bhv_vnodeops { ...@@ -254,8 +250,6 @@ typedef struct bhv_vnodeops {
VOP(vop_read, vp)(VNHEAD(vp),file,iov,segs,offset,ioflags,cr) VOP(vop_read, vp)(VNHEAD(vp),file,iov,segs,offset,ioflags,cr)
#define bhv_vop_write(vp,file,iov,segs,offset,ioflags,cr) \ #define bhv_vop_write(vp,file,iov,segs,offset,ioflags,cr) \
VOP(vop_write, vp)(VNHEAD(vp),file,iov,segs,offset,ioflags,cr) VOP(vop_write, vp)(VNHEAD(vp),file,iov,segs,offset,ioflags,cr)
#define bhv_vop_sendfile(vp,f,off,ioflags,cnt,act,targ,cr) \
VOP(vop_sendfile, vp)(VNHEAD(vp),f,off,ioflags,cnt,act,targ,cr)
#define bhv_vop_splice_read(vp,f,o,pipe,cnt,fl,iofl,cr) \ #define bhv_vop_splice_read(vp,f,o,pipe,cnt,fl,iofl,cr) \
VOP(vop_splice_read, vp)(VNHEAD(vp),f,o,pipe,cnt,fl,iofl,cr) VOP(vop_splice_read, vp)(VNHEAD(vp),f,o,pipe,cnt,fl,iofl,cr)
#define bhv_vop_splice_write(vp,f,o,pipe,cnt,fl,iofl,cr) \ #define bhv_vop_splice_write(vp,f,o,pipe,cnt,fl,iofl,cr) \
......
...@@ -4680,9 +4680,6 @@ bhv_vnodeops_t xfs_vnodeops = { ...@@ -4680,9 +4680,6 @@ bhv_vnodeops_t xfs_vnodeops = {
.vop_open = xfs_open, .vop_open = xfs_open,
.vop_close = xfs_close, .vop_close = xfs_close,
.vop_read = xfs_read, .vop_read = xfs_read,
#ifdef HAVE_SENDFILE
.vop_sendfile = xfs_sendfile,
#endif
#ifdef HAVE_SPLICE #ifdef HAVE_SPLICE
.vop_splice_read = xfs_splice_read, .vop_splice_read = xfs_splice_read,
.vop_splice_write = xfs_splice_write, .vop_splice_write = xfs_splice_write,
......
...@@ -1054,7 +1054,7 @@ struct block_device_operations { ...@@ -1054,7 +1054,7 @@ struct block_device_operations {
}; };
/* /*
* "descriptor" for what we're up to with a read for sendfile(). * "descriptor" for what we're up to with a read.
* This allows us to use the same read code yet * This allows us to use the same read code yet
* have multiple different users of the data that * have multiple different users of the data that
* we read from a file. * we read from a file.
...@@ -1105,7 +1105,6 @@ struct file_operations { ...@@ -1105,7 +1105,6 @@ struct file_operations {
int (*aio_fsync) (struct kiocb *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int); int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *); int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int); int (*check_flags)(int);
...@@ -1762,7 +1761,6 @@ extern ssize_t generic_file_buffered_write(struct kiocb *, const struct iovec *, ...@@ -1762,7 +1761,6 @@ extern ssize_t generic_file_buffered_write(struct kiocb *, const struct iovec *,
unsigned long, loff_t, loff_t *, size_t, ssize_t); unsigned long, loff_t, loff_t *, size_t, ssize_t);
extern ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos); extern ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos);
extern ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos); extern ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos);
extern ssize_t generic_file_sendfile(struct file *, loff_t *, size_t, read_actor_t, void *);
extern void do_generic_mapping_read(struct address_space *mapping, extern void do_generic_mapping_read(struct address_space *mapping,
struct file_ra_state *, struct file *, struct file_ra_state *, struct file *,
loff_t *, read_descriptor_t *, read_actor_t); loff_t *, read_descriptor_t *, read_actor_t);
...@@ -1792,9 +1790,6 @@ extern int nonseekable_open(struct inode * inode, struct file * filp); ...@@ -1792,9 +1790,6 @@ extern int nonseekable_open(struct inode * inode, struct file * filp);
#ifdef CONFIG_FS_XIP #ifdef CONFIG_FS_XIP
extern ssize_t xip_file_read(struct file *filp, char __user *buf, size_t len, extern ssize_t xip_file_read(struct file *filp, char __user *buf, size_t len,
loff_t *ppos); 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 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, extern ssize_t xip_file_write(struct file *filp, const char __user *buf,
size_t len, loff_t *ppos); size_t len, loff_t *ppos);
......
...@@ -9,13 +9,39 @@ ...@@ -9,13 +9,39 @@
#define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */ #define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */
#define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */ #define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */
/**
* struct pipe_buffer - a linux kernel pipe buffer
* @page: the page containing the data for the pipe buffer
* @offset: offset of data inside the @page
* @len: length of data inside the @page
* @ops: operations associated with this buffer. See @pipe_buf_operations.
* @flags: pipe buffer flags. See above.
* @private: private data owned by the ops.
**/
struct pipe_buffer { struct pipe_buffer {
struct page *page; struct page *page;
unsigned int offset, len; unsigned int offset, len;
const struct pipe_buf_operations *ops; const struct pipe_buf_operations *ops;
unsigned int flags; unsigned int flags;
unsigned long private;
}; };
/**
* struct pipe_inode_info - a linux kernel pipe
* @wait: reader/writer wait point in case of empty/full pipe
* @nrbufs: the number of non-empty pipe buffers in this pipe
* @curbuf: the current pipe buffer entry
* @tmp_page: cached released page
* @readers: number of current readers of this pipe
* @writers: number of current writers of this pipe
* @waiting_writers: number of writers blocked waiting for room
* @r_counter: reader counter
* @w_counter: writer counter
* @fasync_readers: reader side fasync
* @fasync_writers: writer side fasync
* @inode: inode this pipe is attached to
* @bufs: the circular array of pipe buffers
**/
struct pipe_inode_info { struct pipe_inode_info {
wait_queue_head_t wait; wait_queue_head_t wait;
unsigned int nrbufs, curbuf; unsigned int nrbufs, curbuf;
...@@ -34,22 +60,73 @@ struct pipe_inode_info { ...@@ -34,22 +60,73 @@ struct pipe_inode_info {
/* /*
* Note on the nesting of these functions: * Note on the nesting of these functions:
* *
* ->pin() * ->confirm()
* ->steal() * ->steal()
* ... * ...
* ->map() * ->map()
* ... * ...
* ->unmap() * ->unmap()
* *
* That is, ->map() must be called on a pinned buffer, same goes for ->steal(). * That is, ->map() must be called on a confirmed buffer,
* same goes for ->steal(). See below for the meaning of each
* operation. Also see kerneldoc in fs/pipe.c for the pipe
* and generic variants of these hooks.
*/ */
struct pipe_buf_operations { struct pipe_buf_operations {
/*
* This is set to 1, if the generic pipe read/write may coalesce
* data into an existing buffer. If this is set to 0, a new pipe
* page segment is always used for new data.
*/
int can_merge; int can_merge;
/*
* ->map() returns a virtual address mapping of the pipe buffer.
* The last integer flag reflects whether this should be an atomic
* mapping or not. The atomic map is faster, however you can't take
* page faults before calling ->unmap() again. So if you need to eg
* access user data through copy_to/from_user(), then you must get
* a non-atomic map. ->map() uses the KM_USER0 atomic slot for
* atomic maps, so you can't map more than one pipe_buffer at once
* and you have to be careful if mapping another page as source
* or destination for a copy (IOW, it has to use something else
* than KM_USER0).
*/
void * (*map)(struct pipe_inode_info *, struct pipe_buffer *, int); void * (*map)(struct pipe_inode_info *, struct pipe_buffer *, int);
/*
* Undoes ->map(), finishes the virtual mapping of the pipe buffer.
*/
void (*unmap)(struct pipe_inode_info *, struct pipe_buffer *, void *); void (*unmap)(struct pipe_inode_info *, struct pipe_buffer *, void *);
int (*pin)(struct pipe_inode_info *, struct pipe_buffer *);
/*
* ->confirm() verifies that the data in the pipe buffer is there
* and that the contents are good. If the pages in the pipe belong
* to a file system, we may need to wait for IO completion in this
* hook. Returns 0 for good, or a negative error value in case of
* error.
*/
int (*confirm)(struct pipe_inode_info *, struct pipe_buffer *);
/*
* When the contents of this pipe buffer has been completely
* consumed by a reader, ->release() is called.
*/
void (*release)(struct pipe_inode_info *, struct pipe_buffer *); void (*release)(struct pipe_inode_info *, struct pipe_buffer *);
/*
* Attempt to take ownership of the pipe buffer and its contents.
* ->steal() returns 0 for success, in which case the contents
* of the pipe (the buf->page) is locked and now completely owned
* by the caller. The page may then be transferred to a different
* mapping, the most often used case is insertion into different
* file address space cache.
*/
int (*steal)(struct pipe_inode_info *, struct pipe_buffer *); int (*steal)(struct pipe_inode_info *, struct pipe_buffer *);
/*
* Get a reference to the pipe buffer.
*/
void (*get)(struct pipe_inode_info *, struct pipe_buffer *); void (*get)(struct pipe_inode_info *, struct pipe_buffer *);
}; };
...@@ -68,39 +145,7 @@ void __free_pipe_info(struct pipe_inode_info *); ...@@ -68,39 +145,7 @@ void __free_pipe_info(struct pipe_inode_info *);
void *generic_pipe_buf_map(struct pipe_inode_info *, struct pipe_buffer *, int); void *generic_pipe_buf_map(struct pipe_inode_info *, struct pipe_buffer *, int);
void generic_pipe_buf_unmap(struct pipe_inode_info *, struct pipe_buffer *, void *); void generic_pipe_buf_unmap(struct pipe_inode_info *, struct pipe_buffer *, void *);
void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *); void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *);
int generic_pipe_buf_pin(struct pipe_inode_info *, struct pipe_buffer *); int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *);
int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *);
/*
* splice is tied to pipes as a transport (at least for now), so we'll just
* add the splice flags here.
*/
#define SPLICE_F_MOVE (0x01) /* move pages instead of copying */
#define SPLICE_F_NONBLOCK (0x02) /* don't block on the pipe splicing (but */
/* we may still block on the fd we splice */
/* from/to, of course */
#define SPLICE_F_MORE (0x04) /* expect more data */
#define SPLICE_F_GIFT (0x08) /* pages passed in are a gift */
/*
* 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 */
};
typedef int (splice_actor)(struct pipe_inode_info *, struct pipe_buffer *,
struct splice_desc *);
extern ssize_t splice_from_pipe(struct pipe_inode_info *, struct file *,
loff_t *, size_t, unsigned int,
splice_actor *);
extern ssize_t __splice_from_pipe(struct pipe_inode_info *, struct file *,
loff_t *, size_t, unsigned int,
splice_actor *);
#endif #endif
/*
* Function declerations and data structures related to the splice
* implementation.
*
* Copyright (C) 2007 Jens Axboe <jens.axboe@oracle.com>
*
*/
#ifndef SPLICE_H
#define SPLICE_H
#include <linux/pipe_fs_i.h>
/*
* splice is tied to pipes as a transport (at least for now), so we'll just
* add the splice flags here.
*/
#define SPLICE_F_MOVE (0x01) /* move pages instead of copying */
#define SPLICE_F_NONBLOCK (0x02) /* don't block on the pipe splicing (but */
/* we may still block on the fd we splice */
/* from/to, of course */
#define SPLICE_F_MORE (0x04) /* expect more data */
#define SPLICE_F_GIFT (0x08) /* pages passed in are a gift */
/*
* Passed to the actors
*/
struct splice_desc {
unsigned int len, total_len; /* current and remaining length */
unsigned int flags; /* splice flags */
/*
* actor() private data
*/
union {
void __user *userptr; /* memory to write to */
struct file *file; /* file to read/write */
void *data; /* cookie */
} u;
loff_t pos; /* file position */
};
struct partial_page {
unsigned int offset;
unsigned int len;
unsigned long private;
};
/*
* Passed to splice_to_pipe
*/
struct splice_pipe_desc {
struct page **pages; /* page map */
struct partial_page *partial; /* pages[] may not be contig */
int nr_pages; /* number of pages in map */
unsigned int flags; /* splice flags */
const struct pipe_buf_operations *ops;/* ops associated with output pipe */
};
typedef int (splice_actor)(struct pipe_inode_info *, struct pipe_buffer *,
struct splice_desc *);
typedef int (splice_direct_actor)(struct pipe_inode_info *,
struct splice_desc *);
extern ssize_t splice_from_pipe(struct pipe_inode_info *, struct file *,
loff_t *, size_t, unsigned int,
splice_actor *);
extern ssize_t __splice_from_pipe(struct pipe_inode_info *,
struct splice_desc *, splice_actor *);
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 *,
splice_direct_actor *);
#endif
...@@ -253,7 +253,7 @@ struct svc_rqst { ...@@ -253,7 +253,7 @@ struct svc_rqst {
* determine what device number * determine what device number
* to report (real or virtual) * to report (real or virtual)
*/ */
int rq_sendfile_ok; /* turned off in gss privacy int rq_splice_ok; /* turned off in gss privacy
* to prevent encrypting page * to prevent encrypting page
* cache pages */ * cache pages */
wait_queue_head_t rq_wait; /* synchronization */ wait_queue_head_t rq_wait; /* synchronization */
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/splice.h>
/* list of open channels, for cpu hotplug */ /* list of open channels, for cpu hotplug */
static DEFINE_MUTEX(relay_channels_mutex); static DEFINE_MUTEX(relay_channels_mutex);
...@@ -121,6 +122,7 @@ static void *relay_alloc_buf(struct rchan_buf *buf, size_t *size) ...@@ -121,6 +122,7 @@ static void *relay_alloc_buf(struct rchan_buf *buf, size_t *size)
buf->page_array[i] = alloc_page(GFP_KERNEL); buf->page_array[i] = alloc_page(GFP_KERNEL);
if (unlikely(!buf->page_array[i])) if (unlikely(!buf->page_array[i]))
goto depopulate; goto depopulate;
set_page_private(buf->page_array[i], (unsigned long)buf);
} }
mem = vmap(buf->page_array, n_pages, VM_MAP, PAGE_KERNEL); mem = vmap(buf->page_array, n_pages, VM_MAP, PAGE_KERNEL);
if (!mem) if (!mem)
...@@ -970,43 +972,6 @@ static int subbuf_read_actor(size_t read_start, ...@@ -970,43 +972,6 @@ static int subbuf_read_actor(size_t read_start,
return ret; return ret;
} }
/*
* subbuf_send_actor - send up to one subbuf's worth of data
*/
static int subbuf_send_actor(size_t read_start,
struct rchan_buf *buf,
size_t avail,
read_descriptor_t *desc,
read_actor_t actor)
{
unsigned long pidx, poff;
unsigned int subbuf_pages;
int ret = 0;
subbuf_pages = buf->chan->alloc_size >> PAGE_SHIFT;
pidx = (read_start / PAGE_SIZE) % subbuf_pages;
poff = read_start & ~PAGE_MASK;
while (avail) {
struct page *p = buf->page_array[pidx];
unsigned int len;
len = PAGE_SIZE - poff;
if (len > avail)
len = avail;
len = actor(desc, p, poff, len);
if (desc->error)
break;
avail -= len;
ret += len;
poff = 0;
pidx = (pidx + 1) % subbuf_pages;
}
return ret;
}
typedef int (*subbuf_actor_t) (size_t read_start, typedef int (*subbuf_actor_t) (size_t read_start,
struct rchan_buf *buf, struct rchan_buf *buf,
size_t avail, size_t avail,
...@@ -1067,19 +1032,159 @@ static ssize_t relay_file_read(struct file *filp, ...@@ -1067,19 +1032,159 @@ static ssize_t relay_file_read(struct file *filp,
NULL, &desc); NULL, &desc);
} }
static ssize_t relay_file_sendfile(struct file *filp, static void relay_consume_bytes(struct rchan_buf *rbuf, int bytes_consumed)
loff_t *ppos,
size_t count,
read_actor_t actor,
void *target)
{ {
read_descriptor_t desc; rbuf->bytes_consumed += bytes_consumed;
desc.written = 0;
desc.count = count; if (rbuf->bytes_consumed >= rbuf->chan->subbuf_size) {
desc.arg.data = target; relay_subbufs_consumed(rbuf->chan, rbuf->cpu, 1);
desc.error = 0; rbuf->bytes_consumed %= rbuf->chan->subbuf_size;
return relay_file_read_subbufs(filp, ppos, subbuf_send_actor, }
actor, &desc); }
static void relay_pipe_buf_release(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
struct rchan_buf *rbuf;
rbuf = (struct rchan_buf *)page_private(buf->page);
relay_consume_bytes(rbuf, buf->private);
}
static struct pipe_buf_operations relay_pipe_buf_ops = {
.can_merge = 0,
.map = generic_pipe_buf_map,
.unmap = generic_pipe_buf_unmap,
.confirm = generic_pipe_buf_confirm,
.release = relay_pipe_buf_release,
.steal = generic_pipe_buf_steal,
.get = generic_pipe_buf_get,
};
/**
* subbuf_splice_actor - splice up to one subbuf's worth of data
*/
static int subbuf_splice_actor(struct file *in,
loff_t *ppos,
struct pipe_inode_info *pipe,
size_t len,
unsigned int flags,
int *nonpad_ret)
{
unsigned int pidx, poff, total_len, subbuf_pages, ret;
struct rchan_buf *rbuf = in->private_data;
unsigned int subbuf_size = rbuf->chan->subbuf_size;
size_t read_start = ((size_t)*ppos) % rbuf->chan->alloc_size;
size_t read_subbuf = read_start / subbuf_size;
size_t padding = rbuf->padding[read_subbuf];
size_t nonpad_end = read_subbuf * subbuf_size + subbuf_size - padding;
struct page *pages[PIPE_BUFFERS];
struct partial_page partial[PIPE_BUFFERS];
struct splice_pipe_desc spd = {
.pages = pages,
.nr_pages = 0,
.partial = partial,
.flags = flags,
.ops = &relay_pipe_buf_ops,
};
if (rbuf->subbufs_produced == rbuf->subbufs_consumed)
return 0;
/*
* Adjust read len, if longer than what is available
*/
if (len > (subbuf_size - read_start % subbuf_size))
len = subbuf_size - read_start % subbuf_size;
subbuf_pages = rbuf->chan->alloc_size >> PAGE_SHIFT;
pidx = (read_start / PAGE_SIZE) % subbuf_pages;
poff = read_start & ~PAGE_MASK;
for (total_len = 0; spd.nr_pages < subbuf_pages; spd.nr_pages++) {
unsigned int this_len, this_end, private;
unsigned int cur_pos = read_start + total_len;
if (!len)
break;
this_len = min_t(unsigned long, len, PAGE_SIZE - poff);
private = this_len;
spd.pages[spd.nr_pages] = rbuf->page_array[pidx];
spd.partial[spd.nr_pages].offset = poff;
this_end = cur_pos + this_len;
if (this_end >= nonpad_end) {
this_len = nonpad_end - cur_pos;
private = this_len + padding;
}
spd.partial[spd.nr_pages].len = this_len;
spd.partial[spd.nr_pages].private = private;
len -= this_len;
total_len += this_len;
poff = 0;
pidx = (pidx + 1) % subbuf_pages;
if (this_end >= nonpad_end) {
spd.nr_pages++;
break;
}
}
if (!spd.nr_pages)
return 0;
ret = *nonpad_ret = splice_to_pipe(pipe, &spd);
if (ret < 0 || ret < total_len)
return ret;
if (read_start + ret == nonpad_end)
ret += padding;
return ret;
}
static ssize_t relay_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;
int nonpad_ret = 0;
ret = 0;
spliced = 0;
while (len) {
ret = subbuf_splice_actor(in, ppos, pipe, len, flags, &nonpad_ret);
if (ret < 0)
break;
else if (!ret) {
if (spliced)
break;
if (flags & SPLICE_F_NONBLOCK) {
ret = -EAGAIN;
break;
}
}
*ppos += ret;
if (ret > len)
len = 0;
else
len -= ret;
spliced += nonpad_ret;
nonpad_ret = 0;
}
if (spliced)
return spliced;
return ret;
} }
const struct file_operations relay_file_operations = { const struct file_operations relay_file_operations = {
...@@ -1089,7 +1194,7 @@ const struct file_operations relay_file_operations = { ...@@ -1089,7 +1194,7 @@ const struct file_operations relay_file_operations = {
.read = relay_file_read, .read = relay_file_read,
.llseek = no_llseek, .llseek = no_llseek,
.release = relay_file_release, .release = relay_file_release,
.sendfile = relay_file_sendfile, .splice_read = relay_file_splice_read,
}; };
EXPORT_SYMBOL_GPL(relay_file_operations); EXPORT_SYMBOL_GPL(relay_file_operations);
......
...@@ -1245,26 +1245,6 @@ int file_send_actor(read_descriptor_t * desc, struct page *page, unsigned long o ...@@ -1245,26 +1245,6 @@ int file_send_actor(read_descriptor_t * desc, struct page *page, unsigned long o
return written; return written;
} }
ssize_t generic_file_sendfile(struct file *in_file, loff_t *ppos,
size_t count, read_actor_t actor, void *target)
{
read_descriptor_t desc;
if (!count)
return 0;
desc.written = 0;
desc.count = count;
desc.arg.data = target;
desc.error = 0;
do_generic_file_read(in_file, ppos, &desc, actor);
if (desc.written)
return desc.written;
return desc.error;
}
EXPORT_SYMBOL(generic_file_sendfile);
static ssize_t static ssize_t
do_readahead(struct address_space *mapping, struct file *filp, do_readahead(struct address_space *mapping, struct file *filp,
unsigned long index, unsigned long nr) unsigned long index, unsigned long nr)
......
...@@ -159,28 +159,6 @@ xip_file_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) ...@@ -159,28 +159,6 @@ xip_file_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
} }
EXPORT_SYMBOL_GPL(xip_file_read); 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)
{
read_descriptor_t desc;
if (!count)
return 0;
desc.written = 0;
desc.count = count;
desc.arg.data = target;
desc.error = 0;
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;
}
EXPORT_SYMBOL_GPL(xip_file_sendfile);
/* /*
* __xip_unmap is invoked from xip_unmap and * __xip_unmap is invoked from xip_unmap and
* xip_write * xip_write
......
...@@ -1100,9 +1100,9 @@ static int shmem_getpage(struct inode *inode, unsigned long idx, ...@@ -1100,9 +1100,9 @@ static int shmem_getpage(struct inode *inode, unsigned long idx,
* Normally, filepage is NULL on entry, and either found * Normally, filepage is NULL on entry, and either found
* uptodate immediately, or allocated and zeroed, or read * uptodate immediately, or allocated and zeroed, or read
* in under swappage, which is then assigned to filepage. * in under swappage, which is then assigned to filepage.
* But shmem_prepare_write passes in a locked filepage, * But shmem_readpage and shmem_prepare_write pass in a locked
* which may be found not uptodate by other callers too, * filepage, which may be found not uptodate by other callers
* and may need to be copied from the swappage read in. * too, and may need to be copied from the swappage read in.
*/ */
repeat: repeat:
if (!filepage) if (!filepage)
...@@ -1485,9 +1485,18 @@ static const struct inode_operations shmem_symlink_inode_operations; ...@@ -1485,9 +1485,18 @@ static const struct inode_operations shmem_symlink_inode_operations;
static const struct inode_operations shmem_symlink_inline_operations; static const struct inode_operations shmem_symlink_inline_operations;
/* /*
* Normally tmpfs makes no use of shmem_prepare_write, but it * Normally tmpfs avoids the use of shmem_readpage and shmem_prepare_write;
* lets a tmpfs file be used read-write below the loop driver. * but providing them allows a tmpfs file to be used for splice, sendfile, and
* below the loop driver, in the generic fashion that many filesystems support.
*/ */
static int shmem_readpage(struct file *file, struct page *page)
{
struct inode *inode = page->mapping->host;
int error = shmem_getpage(inode, page->index, &page, SGP_CACHE, NULL);
unlock_page(page);
return error;
}
static int static int
shmem_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) shmem_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
{ {
...@@ -1711,25 +1720,6 @@ static ssize_t shmem_file_read(struct file *filp, char __user *buf, size_t count ...@@ -1711,25 +1720,6 @@ static ssize_t shmem_file_read(struct file *filp, char __user *buf, size_t count
return desc.error; return desc.error;
} }
static ssize_t shmem_file_sendfile(struct file *in_file, loff_t *ppos,
size_t count, read_actor_t actor, void *target)
{
read_descriptor_t desc;
if (!count)
return 0;
desc.written = 0;
desc.count = count;
desc.arg.data = target;
desc.error = 0;
do_shmem_file_read(in_file, ppos, &desc, actor);
if (desc.written)
return desc.written;
return desc.error;
}
static int shmem_statfs(struct dentry *dentry, struct kstatfs *buf) static int shmem_statfs(struct dentry *dentry, struct kstatfs *buf)
{ {
struct shmem_sb_info *sbinfo = SHMEM_SB(dentry->d_sb); struct shmem_sb_info *sbinfo = SHMEM_SB(dentry->d_sb);
...@@ -2386,6 +2376,7 @@ static const struct address_space_operations shmem_aops = { ...@@ -2386,6 +2376,7 @@ static const struct address_space_operations shmem_aops = {
.writepage = shmem_writepage, .writepage = shmem_writepage,
.set_page_dirty = __set_page_dirty_no_writeback, .set_page_dirty = __set_page_dirty_no_writeback,
#ifdef CONFIG_TMPFS #ifdef CONFIG_TMPFS
.readpage = shmem_readpage,
.prepare_write = shmem_prepare_write, .prepare_write = shmem_prepare_write,
.commit_write = simple_commit_write, .commit_write = simple_commit_write,
#endif #endif
...@@ -2399,7 +2390,8 @@ static const struct file_operations shmem_file_operations = { ...@@ -2399,7 +2390,8 @@ static const struct file_operations shmem_file_operations = {
.read = shmem_file_read, .read = shmem_file_read,
.write = shmem_file_write, .write = shmem_file_write,
.fsync = simple_sync_file, .fsync = simple_sync_file,
.sendfile = shmem_file_sendfile, .splice_read = generic_file_splice_read,
.splice_write = generic_file_splice_write,
#endif #endif
}; };
......
...@@ -853,7 +853,7 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs ...@@ -853,7 +853,7 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs
u32 priv_len, maj_stat; u32 priv_len, maj_stat;
int pad, saved_len, remaining_len, offset; int pad, saved_len, remaining_len, offset;
rqstp->rq_sendfile_ok = 0; rqstp->rq_splice_ok = 0;
priv_len = svc_getnl(&buf->head[0]); priv_len = svc_getnl(&buf->head[0]);
if (rqstp->rq_deferred) { if (rqstp->rq_deferred) {
......
...@@ -814,7 +814,7 @@ svc_process(struct svc_rqst *rqstp) ...@@ -814,7 +814,7 @@ svc_process(struct svc_rqst *rqstp)
rqstp->rq_res.tail[0].iov_base = NULL; rqstp->rq_res.tail[0].iov_base = NULL;
rqstp->rq_res.tail[0].iov_len = 0; rqstp->rq_res.tail[0].iov_len = 0;
/* Will be turned off only in gss privacy case: */ /* Will be turned off only in gss privacy case: */
rqstp->rq_sendfile_ok = 1; rqstp->rq_splice_ok = 1;
/* tcp needs a space for the record length... */ /* tcp needs a space for the record length... */
if (rqstp->rq_prot == IPPROTO_TCP) if (rqstp->rq_prot == IPPROTO_TCP)
svc_putnl(resv, 0); svc_putnl(resv, 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment