aboutsummaryrefslogtreecommitdiff
path: root/fs/splice.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/splice.c')
-rw-r--r--fs/splice.c1295
1 files changed, 1266 insertions, 29 deletions
diff --git a/fs/splice.c b/fs/splice.c
index 7394e9e1753..4dc75c96334 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -30,6 +30,33 @@
#include <linux/syscalls.h>
#include <linux/uio.h>
#include <linux/security.h>
+#include <asm/current.h>
+//#define DEBUG_SPLICE 1
+//#define DEBUG_SPLICE_DMA 1
+#define SPLICE_MAGIC 0xDEADBEEF
+
+
+#ifdef CONFIG_SPLICE_DMA
+static DEFINE_MUTEX(splice_dma_lock);
+extern int splice_dma_memcpy(struct splice_dma_desc *sd_p, unsigned int len);
+extern int
+ppc460ex_sgdma_pipebufs_memcpy(struct pipe_inode_info *pipe, void *dest, unsigned int length);
+#endif
+
+
+
+
+#ifdef SPLICE_DEFER_DMA
+#define MAX_SPLICE_WRITES 4
+#define MAX_SPLICE_PIPES (MAX_SPLICE_WRITES + 1)
+static struct splice_dma_desc_defer* splice_write_queue[MAX_SPLICE_WRITES];
+static int splice_queue_ptr = 0;
+static int splice_queue_count = 0;
+static struct splice_pipe_defer* splice_write_pipes[MAX_SPLICE_PIPES];
+static void put_splice_pipe(struct pipe_inode_info *pipe);
+#endif
+
+//#define SPLICE_NO_IO 1
/*
* Attempt to steal a page from a pipe buffer. This should perhaps go into
@@ -190,8 +217,11 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
if (!ret)
ret = -EPIPE;
break;
+ printk("%s:%d No readers breaking\n", __FUNCTION__, __LINE__);
}
+
+ //printk("pipe->nrbufs %d page_nr %d spd->nr_pages %d \n", pipe->nrbufs, page_nr, spd->nr_pages);
if (pipe->nrbufs < PIPE_BUFFERS) {
int newbuf = (pipe->curbuf + pipe->nrbufs) & (PIPE_BUFFERS - 1);
struct pipe_buffer *buf = pipe->bufs + newbuf;
@@ -213,6 +243,7 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
if (!--spd->nr_pages)
break;
+
if (pipe->nrbufs < PIPE_BUFFERS)
continue;
@@ -222,12 +253,14 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
if (spd->flags & SPLICE_F_NONBLOCK) {
if (!ret)
ret = -EAGAIN;
+
break;
}
if (signal_pending(current)) {
if (!ret)
ret = -ERESTARTSYS;
+
break;
}
@@ -255,7 +288,7 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
while (page_nr < spd_pages)
spd->spd_release(spd, page_nr++);
-
+
return ret;
}
@@ -795,6 +828,843 @@ int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_desc *sd,
}
EXPORT_SYMBOL(splice_from_pipe_feed);
+
+static void
+release_splice_pipebufs_special(struct pipe_inode_info *pipe)
+{
+
+ for (;;) {
+ if (pipe->nrbufs) {
+ struct pipe_buffer *buf = pipe->bufs + pipe->curbuf;
+ const struct pipe_buf_operations *ops = buf->ops;
+ buf->len = 0;
+ ops->release(pipe, buf);
+ pipe->curbuf = (pipe->curbuf + 1) & (PIPE_BUFFERS - 1);
+ pipe->nrbufs--;
+ }
+ else
+ break;
+ }
+}
+
+
+/**
+ * __splice_from_pipe_special - splice data from a pipe to given actor
+ * @pipe: pipe to splice from
+ * @sd: information to @actor
+ * @actor: handler that splices the data
+ *
+ * Description:
+ * This function does little more than loop over the pipe and call
+ * @actor to do the actual moving of a single struct pipe_buffer to
+ * the desired destination. See pipe_to_file, pipe_to_sendpage, or
+ * pipe_to_user. This function is a modified form of __splice_from_pipe
+ * and is customized for splice writes.
+ *
+ */
+ssize_t __splice_from_pipe_special(struct pipe_inode_info *pipe, struct splice_desc *sd,
+ splice_actor *actor)
+{
+ int ret, retval, do_wakeup, len, not_copied, splice_size, to_copy, remaining;
+ unsigned int offset;
+ void *fsdata;
+ struct file *file = sd->u.file;
+ struct address_space *mapping = file->f_mapping;
+ loff_t page_pos = sd->pos;
+ int nrbufs = 0;
+
+ ret = 0;
+ retval = 0;
+ do_wakeup = 0;
+ len = sd->total_len;
+ sd->len = len;
+ nrbufs = pipe->nrbufs;
+ remaining = 0;
+ offset = sd->pos & ~PAGE_CACHE_MASK;
+
+ if (signal_pending(current)) {
+ release_splice_pipebufs_special(pipe);
+ return -ERESTARTSYS;
+ }
+
+#if 0
+ printk("%s:%s:%d len = %d, pipe->nrbufs = %d sd->pos=%lx\n",
+ __FILE__, __FUNCTION__, __LINE__, len, pipe->nrbufs, sd->pos);
+
+
+ printk("%s:%s:%d len = %d, pipe->nrbufs = %d\n",
+ __FILE__, __FUNCTION__, __LINE__, len, pipe->nrbufs);
+
+ for(i=pipe->curbuf; i<pipe->nrbufs; i++) {
+ struct pipe_buffer *pbuf = pipe->bufs + i;
+ printk("%s:%s:%d curbuf=%d pbuf->len=%d addr = %p\n",
+ __FILE__, __FUNCTION__, __LINE__, pipe->curbuf + i,
+ pbuf->len, (page_address(pbuf->page) + pbuf->offset));
+ }
+#endif
+
+ for (;;) {
+ if (pipe->nrbufs) {
+
+ struct pipe_buffer *buf = pipe->bufs + pipe->curbuf;
+ const struct pipe_buf_operations *ops = buf->ops;
+ offset = sd->pos & ~PAGE_CACHE_MASK;
+
+ if(len/PAGE_SIZE)
+ not_copied = PAGE_SIZE;
+ else
+ not_copied = len;
+
+
+ if (not_copied + offset > PAGE_CACHE_SIZE)
+ not_copied = PAGE_CACHE_SIZE - offset;
+
+ splice_size = not_copied;
+ struct page *page = NULL;
+ ret = pagecache_write_begin(file, mapping, page_pos, splice_size,
+ AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata);
+
+
+ if (unlikely(ret))
+ goto out;
+
+ char *dst = kmap(page);
+ while(not_copied) {
+ buf = pipe->bufs + pipe->curbuf;
+ ops = buf->ops;
+
+#if 0
+ printk("%s:%s:%d buf->len=%d pipe->nrbufs=%d\n",
+ __FILE__, __FUNCTION__, __LINE__, buf->len, pipe->nrbufs);
+
+ ret = buf->ops->confirm(pipe, buf);
+ if (unlikely(ret))
+ return ret;
+#endif
+
+ char *src = buf->ops->map(pipe, buf, 0);
+ offset = sd->pos & ~PAGE_CACHE_MASK;
+
+ if(not_copied >= buf->len) {
+ to_copy = buf->len;
+ if(remaining) {
+ to_copy = remaining;
+ }
+ //memcpy(dst + offset, src + buf->offset, to_copy);
+ cacheable_memcpy(dst + offset, src + buf->offset, to_copy);
+ not_copied -= to_copy;
+ remaining = 0;
+ buf->offset += to_copy;
+ buf->len -= to_copy;
+ buf->ops->unmap(pipe, buf, src);
+ ops->release(pipe, buf);
+ pipe->curbuf = (pipe->curbuf + 1) & (PIPE_BUFFERS - 1);
+ pipe->nrbufs--;
+ if(!pipe->nrbufs)
+ break;
+ }
+ else {
+ to_copy = not_copied;
+ //memcpy(dst + offset, src + buf->offset, not_copied);
+ cacheable_memcpy(dst + offset, src + buf->offset, not_copied);
+ remaining = buf->len - not_copied;
+ buf->offset += to_copy;
+ buf->len -= to_copy;
+ not_copied = 0;
+ buf->ops->unmap(pipe, buf, src);
+ }
+ sd->pos += to_copy;
+ }
+ kunmap(page);
+ retval += pagecache_write_end(file, mapping, page_pos, splice_size, splice_size,
+ page, fsdata);
+ len -= splice_size;
+ sd->total_len -= splice_size;
+ sd->len -= splice_size;
+ page_pos += splice_size;
+ //ret += splice_size;
+
+#if 0
+ printk("%s:%s:%d len=%d pipe->nrbufs=%d splice_size=%d retval=%d\n",
+ __FILE__, __FUNCTION__, __LINE__, len, pipe->nrbufs, splice_size, retval);
+#endif
+
+ if(!len)
+ break;
+ }
+
+ if(!pipe->nrbufs)
+ break;
+
+ if (sd->flags & SPLICE_F_NONBLOCK) {
+ if (!retval)
+ retval = -EAGAIN;
+ break;
+ }
+ }
+
+out:
+ // printk("%s:%s:%d - returning %d\n", __FILE__, __FUNCTION__, __LINE__, retval);
+ return retval;
+}
+
+EXPORT_SYMBOL(__splice_from_pipe_special);
+
+#ifdef CONFIG_SPLICE_DMA
+ssize_t __splice_from_pipe_dma(struct pipe_inode_info *pipe, struct splice_desc *sd,
+ splice_actor *actor)
+{
+ int ret, retval, do_wakeup, len, not_copied, splice_size, to_copy, remaining;
+ int begin_buf;
+ int nrbufs;
+ int mapcount;
+ unsigned int offset;
+ void *fsdata;
+ struct file *file;
+ struct address_space *mapping;
+ loff_t page_pos;
+ struct splice_dma_desc *sd_p = NULL;
+ unsigned long *pipe_addr_map = NULL;
+
+ begin_buf = pipe->curbuf;
+ nrbufs = pipe->nrbufs;
+ file = sd->u.file;
+ mapping = file->f_mapping;
+ page_pos = sd->pos;
+ ret = 0;
+ retval = 0;
+ do_wakeup = 0;
+ len = sd->total_len;
+ sd->len = len;
+ remaining = 0;
+ offset = sd->pos & ~PAGE_CACHE_MASK;
+
+#ifdef SPLICE_NO_IO
+ release_splice_pipebufs_special(pipe);
+ return sd->total_len;
+#endif
+
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ goto cleanup;
+ }
+
+ sd_p = kzalloc(sizeof(*sd_p), GFP_ATOMIC);
+ if(!sd_p) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+
+ pipe_addr_map = kzalloc(sizeof(*pipe_addr_map) * PIPE_BUFFERS, GFP_ATOMIC);
+ if(!pipe_addr_map) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+
+ for (;;) {
+ if (pipe->nrbufs) {
+ struct pipe_buffer *buf = pipe->bufs + pipe->curbuf;
+ const struct pipe_buf_operations *ops = buf->ops;
+ offset = sd->pos & ~PAGE_CACHE_MASK;
+ if(len/PAGE_SIZE)
+ not_copied = PAGE_SIZE;
+ else
+ not_copied = len;
+
+
+ if (not_copied + offset > PAGE_CACHE_SIZE)
+ not_copied = PAGE_CACHE_SIZE - offset;
+
+ splice_size = not_copied;
+ struct page *page = NULL;
+
+ ret = pagecache_write_begin(file, mapping, page_pos, splice_size,
+ AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata);
+
+ if (unlikely(ret)) {
+ retval = ret;
+ goto cleanup;
+ }
+
+ mapcount = 0;
+
+ sd_p->page = page;
+ sd_p->page_offset = page_pos;
+ sd_p->n_elems = 0;
+
+ char *dst = kmap(page);
+
+ while(not_copied) {
+
+ buf = pipe->bufs + pipe->curbuf;
+ ops = buf->ops;
+#if 0
+ printk("%s:%s:%d buf->len=%d pipe->nrbufs=%d\n",
+ __FILE__, __FUNCTION__, __LINE__, buf->len, pipe->nrbufs);
+
+ ret = buf->ops->confirm(pipe, buf);
+ if (unlikely(ret)) {
+ retval = ret;
+ goto cleanup;
+ }
+#endif
+
+ char *src = buf->ops->map(pipe, buf, 0);
+ offset = sd->pos & ~PAGE_CACHE_MASK;
+ pipe_addr_map[pipe->curbuf] = (unsigned long)src;
+
+ if(not_copied >= buf->len) {
+ to_copy = buf->len;
+ if(remaining) {
+ to_copy = remaining;
+ }
+#if 0
+ printk("%s:%s:%d - src=0x%08x dst=0x%08x size=%d\n",
+ __FILE__, __FUNCTION__, __LINE__, src + buf->offset, dst + offset,
+ to_copy);
+#endif
+ sd_p->src_addrs[mapcount] = (unsigned long)(src + buf->offset);
+ sd_p->dst_addrs[mapcount] = (unsigned long)(dst + offset);
+#ifdef DEBUG_SPLICE
+ printk("src data %s\n",(char *)sd_p->src_addrs[mapcount]);
+#endif
+ sd_p->xfr_size[mapcount] = to_copy;
+ mapcount++;
+ sd_p->n_elems++;
+ not_copied -= to_copy;
+ remaining = 0;
+ buf->offset += to_copy;
+ buf->len -= to_copy;
+#if 0
+ printk("%s:%s:%d releasing pipe buf not_copied=%d\n",
+ __FILE__, __FUNCTION__, __LINE__, not_copied);
+#endif
+ pipe->curbuf = (pipe->curbuf + 1) & (PIPE_BUFFERS - 1);
+ pipe->nrbufs--;
+
+ }
+ else {
+ to_copy = not_copied;
+#if 0
+ printk("%s:%s:%d - src=0x%08x dst=0x%08x size=%d\n",
+ __FILE__, __FUNCTION__, __LINE__, src + buf->offset, dst + offset,
+ not_copied);
+#endif
+ sd_p->src_addrs[mapcount] = (unsigned long)(src + buf->offset);
+ sd_p->dst_addrs[mapcount] = (unsigned long)(dst + offset);
+#ifdef DEBUG_SPLICE
+ printk("src data %s\n",(char *)sd_p->src_addrs[mapcount]);
+#endif
+ sd_p->xfr_size[mapcount] = not_copied;
+ mapcount++;
+ sd_p->n_elems++;
+ remaining = buf->len - not_copied;
+ buf->offset += to_copy;
+ buf->len -= to_copy;
+ not_copied = 0;
+ }
+ sd->pos += to_copy;
+ if(!pipe->nrbufs)
+ break;
+ }
+
+ mutex_lock(&splice_dma_lock);
+ ret = splice_dma_memcpy(sd_p, splice_size);
+ mutex_unlock(&splice_dma_lock);
+
+ if(ret) {
+ printk("%s:%s:%d - splice_dma_memcpy failed with %d\n",
+ __FILE__, __FUNCTION__, __LINE__, ret);
+ kunmap(page);
+ pagecache_write_end(file, mapping, page_pos, 0, 0,
+ page, fsdata);
+ retval = ret;
+ goto cleanup;
+ }
+
+ kunmap(page);
+ retval += pagecache_write_end(file, mapping, page_pos, splice_size, splice_size,
+ page, fsdata);
+ len -= splice_size;
+ sd->total_len -= splice_size;
+ sd->len -= splice_size;
+ page_pos += splice_size;
+ //ret += splice_size;
+
+#if 0
+ printk("%s:%s:%d len=%d pipe->nrbufs=%d splice_size=%d retval=%d\n",
+ __FILE__, __FUNCTION__, __LINE__, len, pipe->nrbufs, splice_size, retval);
+#endif
+
+ if(!len)
+ break;
+ }
+
+ if(!pipe->nrbufs)
+ break;
+
+
+ if (sd->flags & SPLICE_F_NONBLOCK) {
+ if (!retval)
+ retval = -EAGAIN;
+ break;
+ }
+ }
+
+cleanup:
+ if(sd_p)
+ kfree(sd_p);
+
+ pipe->curbuf = begin_buf;
+ for (;;) {
+ if (nrbufs) {
+ struct pipe_buffer *buf = pipe->bufs + pipe->curbuf;
+ const struct pipe_buf_operations *ops = buf->ops;
+ buf->len = 0;
+ if(pipe_addr_map && (pipe_addr_map[pipe->curbuf])) {
+ char *src = (char *)(pipe_addr_map[pipe->curbuf]);
+ buf->ops->unmap(pipe, buf, src);
+ }
+ ops->release(pipe, buf);
+ pipe->curbuf = (pipe->curbuf + 1) & (PIPE_BUFFERS - 1);
+ nrbufs--;
+ }
+ if(!nrbufs)
+ break;
+ }
+
+ if(pipe_addr_map)
+ kfree(pipe_addr_map);
+
+// printk("%s:%s:%d - returning %d\n", __FILE__, __FUNCTION__, __LINE__, retval);
+ return retval;
+}
+EXPORT_SYMBOL(__splice_from_pipe_dma);
+#endif
+
+
+#ifdef SPLICE_DEFER_DMA
+
+static int
+splice_setup_xfr_sglist(struct splice_dma_desc_defer *sd_def,
+ struct splice_dma_desc *sd_p, int *mapcount)
+{
+ unsigned int offset;
+ struct splice_desc *sd = &(sd_def->sd);
+ struct pipe_inode_info *pipe = sd_def->pipe;
+ //int curbuf = sd_def->pipe_curbuf;
+ //int nrbufs = sd_def->pipe_nrbufs;
+ struct page *page = sd_def->page;
+ int len = sd->total_len;
+ int count = *mapcount;
+ int ret = 0, n_elems = 0, not_copied = 0, to_copy = 0;
+ int remaining = 0;
+
+ //printk("%s:%s:%d consuming pipe %p\n", __FILE__, __FUNCTION__, __LINE__, pipe);
+
+ offset = sd->pos & ~PAGE_CACHE_MASK;
+ if( (len > PAGE_SIZE) || ((len + offset) > PAGE_CACHE_SIZE)) {
+#if 0
+ printk("%s:%s:%d - filesystem data is not block aligned"
+ "len=%d , offset=0x%08x\n", __FILE__, __FUNCTION__,
+ __LINE__, len, offset);
+#endif
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if(pipe->nrbufs) {
+ struct pipe_buffer *buf = pipe->bufs + pipe->curbuf;
+ const struct pipe_buf_operations *ops = buf->ops;
+ offset = sd->pos & ~PAGE_CACHE_MASK;
+
+ not_copied = len;
+ //printk("%s:%s:%d - calling page_address\n", __FILE__, __FUNCTION__, __LINE__);
+ char *dst = kmap(page);
+#if 0
+ printk("%s:%s:%d - kmap returns %p for page=%p\n",
+ __FILE__, __FUNCTION__, __LINE__, dst, page);
+#endif
+ while(not_copied) {
+ buf = pipe->bufs + pipe->curbuf;
+ ops = buf->ops;
+
+ ret = buf->ops->confirm(pipe, buf);
+ if (unlikely(ret)) {
+ printk("%s:%s:%d - ret is %d\n",
+ __FILE__, __FUNCTION__, __LINE__, ret);
+ goto out;
+ }
+
+ char *src = buf->ops->map(pipe, buf, 0);
+#if 0
+ printk("%s:%s:%d - buf->ops->map returns %p\n",
+ __FILE__, __FUNCTION__, __LINE__, src);
+#endif
+ offset = sd->pos & ~PAGE_CACHE_MASK;
+ sd_def->pipe_addr_map[n_elems++] = (unsigned long)src;
+
+ if(not_copied >= buf->len) {
+ to_copy = buf->len;
+ if(remaining) {
+ to_copy = remaining;
+ }
+ sd_p->src_addrs[count] =
+ (unsigned long)(src + buf->offset);
+ sd_p->dst_addrs[count] =
+ (unsigned long)(dst + offset);
+ sd_p->xfr_size[count] = to_copy;
+#if 0
+ sd_def->src_addrs[count] =
+ (unsigned long)(src + buf->offset);
+ sd_def->dst_addrs[count] =
+ (unsigned long)(dst + offset);
+ sd_def->xfr_size[count] = to_copy;
+#endif
+
+#if 0
+ printk("%s:%s:%d - src=%p, dst=%p, size=%d index=%d sd_p->n_elems=%d\n",
+ __FILE__, __FUNCTION__, __LINE__, sd_p->src_addrs[count],
+ sd_p->dst_addrs[count], sd_p->xfr_size[count], count, sd_p->n_elems);
+#endif
+
+ count++;
+ sd_p->n_elems++;
+ not_copied -= to_copy;
+ remaining = 0;
+ buf->offset += to_copy;
+ buf->len -= to_copy;
+ pipe->curbuf = (pipe->curbuf + 1) & (PIPE_BUFFERS - 1);
+ pipe->nrbufs--;
+ }
+ else {
+ to_copy = not_copied;
+ sd_p->src_addrs[count] =
+ (unsigned long)(src + buf->offset);
+ sd_p->dst_addrs[count] =
+ (unsigned long)(dst + offset);
+ sd_p->xfr_size[count] = not_copied;
+#if 0
+ sd_def->src_addrs[count] =
+ (unsigned long)(src + buf->offset);
+ sd_def->dst_addrs[count] =
+ (unsigned long)(dst + offset);
+ sd_def->xfr_size[count] = not_copied;
+#endif
+
+#if 0
+ printk("%s:%s:%d - src=%p, dst=%p, size=%d index=%d\n",
+ __FILE__, __FUNCTION__, __LINE__, sd_p->src_addrs[count],
+ sd_p->dst_addrs[count], sd_p->xfr_size[count], count);
+#endif
+
+ count++;
+ sd_p->n_elems++;
+ remaining = buf->len - not_copied;
+ buf->offset += to_copy;
+ not_copied = 0;
+ }
+ sd->pos += to_copy;
+ if(!pipe->nrbufs)
+ break;
+ }
+ }
+
+ sd_def->n_elems = n_elems;
+ *mapcount = count;
+
+out:
+ //printk("%s:%s:%d returning %d\n", __FILE__, __FUNCTION__, __LINE__, ret);
+ //printk("%s:%s:%d sd_p->n_elems=%d\n", __FILE__, __FUNCTION__, __LINE__, sd_p->n_elems);
+ return ret;
+}
+
+static void
+release_splice_pipebufs(struct splice_dma_desc_defer *sd_def)
+{
+
+ int n = 0;
+ struct pipe_inode_info *pipe = sd_def->pipe;
+ int nrbufs = sd_def->pipe_nrbufs;
+ int curbuf = sd_def->pipe_curbuf;
+
+ for (;;) {
+ if (nrbufs) {
+ struct pipe_buffer *buf = pipe->bufs + curbuf;
+#if 0
+ printk("%s:%s:%d - releasing buf %d\n",
+ __FILE__, __FUNCTION__, __LINE__, curbuf);
+#endif
+ const struct pipe_buf_operations *ops = buf->ops;
+ buf->len = 0;
+ if(sd_def->pipe_addr_map[n]) {
+ char *src = (char *)(sd_def->pipe_addr_map[n]);
+ buf->ops->unmap(pipe, buf, src);
+ }
+ ops->release(pipe, buf);
+ curbuf = (curbuf + 1) & (PIPE_BUFFERS - 1);
+ nrbufs--;
+ n++;
+ }
+ else
+ break;
+ }
+}
+
+
+static
+int splice_flush_dma_writes(void)
+{
+ loff_t pos;
+ void *fsdata;
+ struct file *file;
+ struct address_space *mapping;
+ int size=0, ret=0, i=0, mapcount=0;
+ struct splice_desc *sd;
+ struct splice_dma_desc_defer *sd_def = NULL;
+ struct page *page = NULL;
+ int total_splice_length = 0;
+ struct pipe_inode_info *pipe = NULL;
+ //printk("%s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);
+ struct splice_dma_desc *sd_p = NULL;
+
+ sd_p = kzalloc(sizeof(*sd_p), GFP_ATOMIC);
+ if(!sd_p)
+ return -ENOMEM;
+
+ for(i=0; i < MAX_SPLICE_WRITES; i++) {
+ sd_def = (struct splice_dma_desc_defer *)splice_write_queue[i];
+ if(sd_def && sd_def->valid) {
+ page = NULL;
+ sd = &(sd_def->sd);
+ pos = sd->pos;
+ file = sd->u.file;
+ mapping = file->f_mapping;
+ size = sd->total_len;
+#if 0
+ printk("%s:%s:%d - calling pagecache_write_begin i=%d\n",
+ __FILE__, __FUNCTION__, __LINE__, i);
+ printk("file is %s , mapping is %s\n",
+ (file) ? "Not NULL":"NULL", (mapping) ? "Not NULL":"NULL");
+#endif
+ ret = pagecache_write_begin(file, mapping, pos, size,
+ AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata);
+ if (unlikely(ret)) {
+ printk("%s:%s:%d - pagecache_write_begin returns %d\n",
+ __FILE__, __FUNCTION__, __LINE__, ret);
+ goto out;
+ }
+ sd_def->page = page;
+ sd_def->fsdata = fsdata;
+ ret = splice_setup_xfr_sglist(sd_def, sd_p, &mapcount);
+#if 0
+ printk("%s:%s:%d - sd_p->n_elems = %d\n",
+ __FILE__, __FUNCTION__, __LINE__, sd_p->n_elems);
+#endif
+ if(ret)
+ goto out;
+ total_splice_length += size;
+ }
+ }
+
+ //printk("%s:%s:%d - nrelems = %d\n", __FILE__, __FUNCTION__, __LINE__, sd_p->n_elems);
+
+ ret = splice_dma_memcpy(sd_p, total_splice_length);
+ if(ret) {
+ printk("%s:%s:%d - splice_dma_memcpy failed with %d\n",
+ __FILE__, __FUNCTION__, __LINE__, ret);
+ goto out;
+ }
+
+ for(i=0; i < MAX_SPLICE_WRITES; i++) {
+ sd_def = splice_write_queue[i];
+ if(sd_def && sd_def->valid) {
+ sd = &(sd_def->sd);
+ page = sd_def->page;
+ pos = sd->pos;
+ file = sd->u.file;
+ mapping = file->f_mapping;
+ size = sd->total_len;
+ fsdata = sd_def->fsdata;
+ pipe = sd_def->pipe;
+ ret = pagecache_write_end(file, mapping, pos,
+ size, size, page, fsdata);
+ sd_def->valid = 0;
+ kunmap(page);
+ release_splice_pipebufs(sd_def);
+#if 0
+ printk("%s:%s:%d - done with pipe %p\n", __FILE__, __FUNCTION__, __LINE__,
+ pipe);
+#endif
+ put_splice_pipe(pipe);
+ //sd_def->pipe = NULL;
+ }
+ }
+
+
+out:
+ //printk("%s:%s:%d returning %d \n", __FILE__, __FUNCTION__, __LINE__, ret);
+ kfree(sd_p);
+ return ret;
+}
+
+
+ssize_t __splice_write_defer(struct pipe_inode_info *pipe, struct splice_desc *sd, splice_actor *actor)
+{
+
+ unsigned int offset;
+ int ret = 0, i = 0;
+ struct splice_dma_desc_defer *sd_def = NULL;
+ int len = sd->total_len;
+ int total_splice_length = 0;
+ int splice_dma_flush = 0;
+ struct file *file = sd->u.file;
+ int same_blk = 0;
+// int curbuf = pipe->curbuf;
+// int nrbufs = pipe->nrbufs;
+
+ offset = sd->pos & ~PAGE_CACHE_MASK;
+
+
+ //printk("%s:%s:%d sd->pos = %llx\n", __FILE__, __FUNCTION__, __LINE__, sd->pos);
+
+ /* un-aligned page writes */
+ if((offset & (~PAGE_CACHE_MASK)) || (len > PAGE_SIZE)) {
+#if 0
+ printk("%s:%s:%d - un-aligned page writes\n",
+ __FILE__, __FUNCTION__, __LINE__);
+#endif
+ return __splice_from_pipe_dma(pipe, sd, actor);
+ }
+
+ if((sd->pos == 0) && (len < PAGE_SIZE))
+ return __splice_from_pipe_dma(pipe, sd, actor);
+#if 0
+ if((splice_queue_ptr + 1) >= MAX_SPLICE_WRITES)
+ splice_dma_flush = 1;
+#endif
+
+ //printk("%s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);
+ /* If write request for same page in same file exists in splice queue */
+ for(i = 0; i < MAX_SPLICE_WRITES; i++) {
+ sd_def = (struct splice_dma_desc_defer *)splice_write_queue[i];
+ if(sd_def && sd_def->valid) {
+ if ((sd_def->sd.u.file == file) &&
+ (sd_def->sd.pos == sd->pos) ) {
+ //printk("%s:%s:%d - same blk writes\n", __FILE__, __FUNCTION__, __LINE__);
+ //pipe->curbuf = sd_def->pipe_curbuf;
+ //pipe->nrbufs = sd_def->pipe_nrbufs;
+ __splice_from_pipe_dma(sd_def->pipe, &(sd_def->sd), actor);
+ sd_def-> valid = 0;
+ //same_blk = 1;
+ --splice_queue_count;
+ //return __splice_from_pipe_dma(pipe, sd, actor);
+ }
+ }
+ }
+
+ //pipe->curbuf = curbuf;
+ //pipe->nrbufs = nrbufs;
+#if 0
+ if(same_blk)
+ splice_dma_flush = 0;
+#endif
+
+ sd_def = splice_write_queue[splice_queue_ptr];
+ if(!sd_def) {
+ /* Fisrt time alloc */
+ sd_def = kzalloc(sizeof(*sd_def), GFP_KERNEL);
+ if(!sd_def) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ splice_write_queue[splice_queue_ptr] = sd_def;
+ }
+
+ sd_def = splice_write_queue[splice_queue_ptr];
+ sd_def->sd.total_len = sd->total_len;
+ sd_def->sd.flags = sd->flags;
+ sd_def->sd.pos = sd->pos;
+ sd_def->sd.u.file = sd->u.file;
+ sd_def->f_offset = offset;
+ sd_def->pipe_curbuf = pipe->curbuf;
+ sd_def->pipe_nrbufs = pipe->nrbufs;
+ sd_def->pipe = pipe;
+ sd_def->valid = 1;
+ ++splice_queue_count;
+#if 0
+ printk("%s:%s:%d - pipe->curbuf=%d , pipe->nrbufs=%d splice_queue_ptr=%d\n",
+ __FILE__, __FUNCTION__, __LINE__, pipe->curbuf, pipe->nrbufs, splice_queue_ptr);
+#endif
+
+ splice_queue_ptr = (splice_queue_ptr + 1) & (MAX_SPLICE_WRITES - 1);
+ if(splice_queue_count >= MAX_SPLICE_WRITES) {
+ /* flush splice queue */
+ //printk("%s:%s:%d - dma flush\n", __FILE__, __FUNCTION__, __LINE__);
+ splice_queue_count = 0;
+ ret = splice_flush_dma_writes();
+ if(ret <= 0)
+ goto out;
+ }
+ //pipe->curbuf = (pipe->curbuf + pipe->nrbufs) & (PIPE_BUFFERS - 1);
+ //pipe->nrbufs = 0;
+ ret = len;
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL(__splice_write_defer);
+
+#endif
+
+/**
+ * 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(). This is a special
+ * implementation fpr receive file.
+ *
+ */
+ssize_t splice_from_pipe_special(struct pipe_inode_info *pipe, struct file *out,
+ loff_t *ppos, size_t len, unsigned int flags,
+ splice_actor *actor)
+{
+ ssize_t ret;
+ struct splice_desc sd = {
+ .total_len = len,
+ .flags = flags,
+ .pos = *ppos,
+ .u.file = out,
+ };
+
+ if (signal_pending(current)) {
+ release_splice_pipebufs_special(pipe);
+ return -ERESTARTSYS;
+ }
+
+ /*
+ * The actor worker might be calling ->prepare_write and
+ * ->commit_write. Most of the time, these expect i_mutex to
+ * be held. Since this may result in an ABBA deadlock with
+ * pipe->inode, we have to order lock acquiry here.
+ */
+ pipe_lock(pipe);
+#ifdef CONFIG_SPLICE_DMA
+ ret = __splice_from_pipe_dma(pipe, &sd, actor);
+#else
+ ret = __splice_from_pipe_special(pipe, &sd, actor);
+#endif
+ pipe_unlock(pipe);
+
+ return ret;
+}
/**
* splice_from_pipe_next - wait for some data to splice from
* @pipe: pipe to splice from
@@ -947,36 +1817,22 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
{
struct address_space *mapping = out->f_mapping;
struct inode *inode = mapping->host;
- struct splice_desc sd = {
- .total_len = len,
- .flags = flags,
- .pos = *ppos,
- .u.file = out,
- };
ssize_t ret;
+ int err = 0;
- pipe_lock(pipe);
-
- splice_from_pipe_begin(&sd);
- do {
- ret = splice_from_pipe_next(pipe, &sd);
- if (ret <= 0)
- break;
-
- mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
- ret = file_remove_suid(out);
- if (!ret) {
- file_update_time(out);
- ret = splice_from_pipe_feed(pipe, &sd, pipe_to_file);
- }
- mutex_unlock(&inode->i_mutex);
- } while (ret > 0);
- splice_from_pipe_end(pipe, &sd);
+ if (signal_pending(current)) {
+ release_splice_pipebufs_special(pipe);
+ return -ERESTARTSYS;
+ }
- pipe_unlock(pipe);
+ err = file_remove_suid(out);
+ if(err)
+ return err;
+ file_update_time(out);
- if (sd.num_spliced)
- ret = sd.num_spliced;
+ mutex_lock(&inode->i_mutex);
+ ret = splice_from_pipe_special(pipe, out, ppos, len, flags, pipe_to_file);
+ mutex_unlock(&inode->i_mutex);
if (ret > 0) {
unsigned long nr_pages;
@@ -1064,6 +1920,11 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
if (unlikely(out->f_flags & O_APPEND))
return -EINVAL;
+ if (signal_pending(current)) {
+ release_splice_pipebufs_special(pipe);
+ return -ERESTARTSYS;
+ }
+
ret = rw_verify_area(WRITE, out, ppos, len);
if (unlikely(ret < 0))
return ret;
@@ -1355,6 +2216,372 @@ static long do_splice(struct file *in, loff_t __user *off_in,
return -EINVAL;
}
+#if 0
+static void print_pipe(struct pipe_inode_info *pipe)
+{
+ int nrbufs = pipe->nrbufs;
+ int i = 0;
+
+ printk("pipe->nrbufs = %d\n", pipe->nrbufs);
+ for(i=0; i<nrbufs; i++)
+ printk("nr = %d, pipe->bufs[i].len = %d, pipe->bufs[i].offset = %d\n",
+ i, pipe->bufs[i].len, pipe->bufs[i].offset);
+}
+
+static void print_data(struct pipe_inode_info *pipe)
+{
+ int i;
+ int nrbufs = pipe->nrbufs;
+ char strbuf[256];
+ struct pipe_buffer *buf = NULL;
+ void *mem = NULL;
+
+
+ for(i=0; i<nrbufs; i++) {
+ buf = &(pipe->bufs[i]);
+ buf->ops->confirm(pipe, buf);
+ mem = (void *)page_address(buf->page);
+ memset(strbuf, 0, 256);
+ memcpy(strbuf, mem, 256);
+ printk("%s:%s:%d - memory in strbuf is %s\n", __FILE__, __FUNCTION__, __LINE__, strbuf);
+ }
+}
+
+static void
+adjust_pipe_splice_bufs(struct pipe_inode_info *pipe, void *splice_mem, int nrpages, int total_len)
+{
+ unsigned long offset;
+ int old_curbuf = pipe->curbuf;
+ int len = total_len;
+
+#ifdef DEBUG_SPLICE
+ printk("%s:%s:%d pipe->nrbufs=%d \n", __FILE__, __FUNCTION__, __LINE__, pipe->nrbufs);
+#endif
+
+
+ if (pipe->inode) {
+ printk("%s:%s:%d - locking pipe->inode->i_mutex\n", __FILE__, __FUNCTION__, __LINE__);
+ mutex_lock(&pipe->inode->i_mutex);
+ }
+
+ for(;;) {
+ if(pipe->nrbufs) {
+ struct pipe_buffer *buf = pipe->bufs + pipe->curbuf;
+ const struct pipe_buf_operations *ops = buf->ops;
+
+ if(buf->len) {
+#ifdef DEBUG_SPLICE
+ printk("%s:%s:%d - freeing buf %d\n",
+ __FILE__, __FUNCTION__, __LINE__, pipe->curbuf);
+#endif
+ buf->len = 0;
+ ops->release(pipe, buf);
+ buf->private = 0;
+ }
+
+ pipe->nrbufs--;
+ pipe->curbuf = (pipe->curbuf + 1) & (PIPE_BUFFERS - 1);
+
+ }
+ else
+ break;
+ }
+
+ pipe->curbuf = old_curbuf;
+
+#ifdef DEBUG_SPLICE
+ printk("%s:%s:%d - pipe->curbuf = %d pipe->nrbufs = %d\n",
+ __FILE__, __FUNCTION__, __LINE__, pipe->curbuf, pipe->nrbufs);
+#endif
+
+ for(;;) {
+ int newbuf = (pipe->curbuf + pipe->nrbufs) & (PIPE_BUFFERS - 1);
+ struct pipe_buffer *buf = pipe->bufs + newbuf;
+
+ offset = PAGE_SIZE * pipe->nrbufs;
+ if(len) {
+ if(len/PAGE_SIZE) {
+ buf->page = virt_to_page(splice_mem + offset);
+ buf->len = PAGE_SIZE;
+ buf->offset = 0;
+ len -= PAGE_SIZE;
+ pipe->nrbufs++;
+ buf->private = SPLICE_MAGIC;
+ }
+ else { /* partial Page */
+ buf->page = virt_to_page(splice_mem + offset);
+ buf->len = len;
+ buf->offset = 0;
+ len = 0;
+ pipe->nrbufs++;
+ buf->private = SPLICE_MAGIC;
+ break;
+ }
+ }
+ else
+ break;
+ }
+
+ if (pipe->inode) {
+ printk("%s:%s:%d - unlocking pipe->inode->i_mutex\n", __FILE__, __FUNCTION__, __LINE__);
+ mutex_unlock(&pipe->inode->i_mutex);
+ }
+}
+
+
+static void
+ppc460ex_pipebufs_memcpy(struct pipe_inode_info *pipe, void *splice_mem, int length)
+{
+ int len = 0;
+ int nrbufs = pipe->nrbufs;
+ void *src = NULL;
+ int curbuf = pipe->curbuf;
+
+ if (pipe->inode) {
+ printk("%s:%s:%d - locking pipe->inode->i_mutex\n", __FILE__, __FUNCTION__, __LINE__);
+ mutex_lock(&pipe->inode->i_mutex);
+ }
+
+ for(;;) {
+ if(nrbufs) {
+ struct pipe_buffer *buf = pipe->bufs + curbuf;
+ if(len < length) {
+ if(!buf->len)
+ continue;
+
+ src = page_address(buf->page);
+ memcpy(splice_mem + len, src+buf->offset, buf->len);
+ //flush_dcache_page(buf->page);
+
+ len += buf->len;
+ curbuf = (curbuf + 1) & (PIPE_BUFFERS - 1);
+ --nrbufs;
+ }
+ else /* all pipe buf copied*/
+ break;
+ }
+ else
+ break;
+ }
+ if (pipe->inode) {
+ printk("%s:%s:%d - locking pipe->inode->i_mutex\n", __FILE__, __FUNCTION__, __LINE__);
+ mutex_unlock(&pipe->inode->i_mutex);
+ }
+}
+
+#endif /* 0 */
+
+#ifdef SPLICE_DEFER_DMA
+
+static void put_splice_pipe(struct pipe_inode_info *pipe)
+{
+#if 0
+ int index = pipe->private;
+ struct splice_pipe_defer *pipe_def = splice_write_pipes[index];
+ pipe_def->available = 1;
+#endif
+ int i = 0;
+ struct splice_pipe_defer *pipe_def = NULL;
+ struct pipe_inode_info *pipe_local = NULL;
+
+ //printk("%s:%s:%d - pipe =%p\n", __FILE__, __FUNCTION__, __LINE__, pipe);
+ for(i=0; i<MAX_SPLICE_PIPES; i++) {
+ pipe_def = (struct splice_pipe_defer *)splice_write_pipes[i];
+ if(pipe_def && pipe_def->pipe) {
+ pipe_local = pipe_def->pipe;
+#if 0
+ printk("%s:%s:%d - pipe_local =%p\n",
+ __FILE__, __FUNCTION__, __LINE__, pipe_local);
+#endif
+ if(pipe == pipe_local) {
+ pipe_def->available = 1;
+ return;
+ }
+ }
+ }
+ printk("%s:%s:%d - pipe not found !!!!\n", __FILE__, __FUNCTION__, __LINE__);
+}
+
+static struct pipe_inode_info *get_splice_pipe(void)
+{
+ int i = 0;
+ struct splice_pipe_defer *pipe_def = NULL;
+ struct pipe_inode_info *pipe = NULL;
+
+ for(i=0; i<MAX_SPLICE_PIPES; i++) {
+ pipe_def = (struct pipe_inode_info *)splice_write_pipes[i];
+ if(pipe_def) {
+ if(pipe_def->available) {
+ pipe_def->available = 0;
+ pipe = pipe_def->pipe;
+ //pipe->private = i;
+ goto out;
+ }
+ }
+ else {
+ pipe_def = kzalloc(sizeof(*pipe_def), GFP_KERNEL);
+ if(!pipe_def) {
+ printk("%s:%s:%d - error allocating memory for splice_pipe_defer\n",
+ __FILE__, __FUNCTION__, __LINE__);
+ goto out;
+ }
+ pipe = alloc_pipe_info(NULL);
+ if (!pipe) {
+ printk("%s %s:%d alloc_pipe_info failed\n",
+ __FUNCTION__, __FILE__, __LINE__);
+ goto out;
+ }
+ pipe_def->pipe = pipe;
+ pipe_def->available = 0;
+ splice_write_pipes[i] = pipe_def;
+ //pipe->private = i;
+ break;
+ }
+ }
+
+out:
+ return pipe;
+}
+#endif
+
+static long do_splice_2(int fd_in, struct file *in, loff_t __user *off_in,
+ int fd_out, struct file *out, loff_t __user *off_out,
+ size_t len, unsigned int flags)
+{
+ struct pipe_inode_info *pipe;
+ loff_t offset, *off;
+ long ret;
+
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+#ifdef DEBUG_SPLICE
+ printk("%s:%s:%d - len=%d\n", __FILE__, __FUNCTION__, __LINE__, len);
+#endif
+
+#ifndef SPLICE_DEFER_DMA
+ pipe = alloc_pipe_info(NULL);
+ if (!pipe) {
+ printk("%s %s:%d alloc_pipe_info failed\n",
+ __FUNCTION__, __FILE__, __LINE__);
+ return -ENOMEM;
+ }
+
+ /*
+ * We don't have an immediate reader, but we'll read the stuff
+ * out of the pipe right after the splice_to_pipe(). So set
+ * PIPE_READERS appropriately.
+ */
+ pipe->readers = 1;
+
+#else
+
+ pipe = get_splice_pipe();
+ if(!pipe)
+ return -ENOMEM;
+
+ pipe->readers = 1;
+#endif
+
+ if (off_in) {
+ printk(KERN_ERR "%s:%s:%d off_in is seekable\n",
+ __FUNCTION__, __FILE__, __LINE__);
+ ret = -ESPIPE;
+ goto out;
+ }
+ if (off_out) {
+ if (out->f_op->llseek == no_llseek) {
+ printk(KERN_ERR "%s:%s:%d out is non-seekable\n",
+ __FUNCTION__, __FILE__, __LINE__);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (copy_from_user(&offset, off_out, sizeof(loff_t))) {
+ printk(KERN_ERR "%s:%s:%d copy_from_user failed\n",
+ __FUNCTION__, __FILE__, __LINE__);
+ ret = -EFAULT;
+ goto out;
+ }
+ off = &offset;
+ } else
+ off = &out->f_pos;
+
+
+#ifdef DEBUG_SPLICE
+ printk("%s:%s:%d - calling in->f_op.splice_read len=%d\n",
+ __FILE__, __FUNCTION__, __LINE__, len);
+#endif
+ ret = in->f_op->splice_read(in, off_in, pipe, len, flags);
+
+#ifdef DEBUG_SPLICE
+ printk("%s:%s:%d - ret from sock_splice_read =%d\n", __FILE__, __FUNCTION__, __LINE__, ret);
+#endif
+
+ if(!ret) {
+ printk(KERN_ERR "%s:%s:%d sock_splice_read read nothing\n",
+ __FILE__, __FUNCTION__, __LINE__);
+ ret = -EAGAIN;
+ goto out;
+ }
+
+
+#ifdef DEBUG_SPLICE
+
+ printk("%s:%s:%d - Number of pipe bufs = %d\n",
+ __FILE__, __FUNCTION__, __LINE__, pipe->nrbufs);
+#endif
+
+ len = ret;
+ ret = do_splice_from(pipe, out, off, len, flags);
+
+#ifdef DEBUG_SPLICE
+ printk("%s:%s:%d do_splice_from returns %d\n", __FILE__, __FUNCTION__, __LINE__,
+ ret);
+#endif
+
+ if (off_out && copy_to_user(off_out, off, sizeof(loff_t))) {
+ printk(KERN_ERR "%s:%s:%d copy_to_user failed\n",
+ __FILE__, __FUNCTION__, __LINE__);
+ ret = -EFAULT;
+ }
+
+#ifdef DEBUG_SPLICE
+ printk("%s:%s:%d returning ret=%d\n", __FILE__, __FUNCTION__, __LINE__, ret);
+#endif
+
+out:
+ kfree(pipe);
+ return ret;
+}
+/*
+ * Do a copy-from-user while holding the mmap_semaphore for reading, in a
+ * manner safe from deadlocking with simultaneous mmap() (grabbing mmap_sem
+ * for writing) and page faulting on the user memory pointed to by src.
+ * This assumes that we will very rarely hit the partial != 0 path, or this
+ * will not be a win.
+ */
+static int copy_from_user_mmap_sem(void *dst, const void __user *src, size_t n)
+{
+ int partial;
+
+ if (!access_ok(VERIFY_READ, src, n))
+ return -EFAULT;
+
+ pagefault_disable();
+ partial = __copy_from_user_inatomic(dst, src, n);
+ pagefault_enable();
+
+ /*
+ * Didn't copy everything, drop the mmap_sem and do a faulting copy
+ */
+ if (unlikely(partial)) {
+ up_read(&current->mm->mmap_sem);
+ partial = copy_from_user(dst, src, n);
+ down_read(&current->mm->mmap_sem);
+ }
+
+ return partial;
+}
/*
* Map an iov into an array of pages and offset/length tupples. With the
* partial_page structure, we can map several non-contiguous ranges into
@@ -1654,23 +2881,33 @@ SYSCALL_DEFINE6(splice, int, fd_in, loff_t __user *, off_in,
int, fd_out, loff_t __user *, off_out,
size_t, len, unsigned int, flags)
{
- long error;
+ long error = 0;
struct file *in, *out;
int fput_in, fput_out;
if (unlikely(!len))
return 0;
- error = -EBADF;
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
in = fget_light(fd_in, &fput_in);
if (in) {
if (in->f_mode & FMODE_READ) {
out = fget_light(fd_out, &fput_out);
if (out) {
if (out->f_mode & FMODE_WRITE)
+#if 0
error = do_splice(in, off_in,
out, off_out,
len, flags);
+#else
+ {
+ error = do_splice_2(fd_in, in, off_in,
+ fd_out, out, off_out,
+ len, flags);
+ }
+#endif
fput_light(out, fput_out);
}
}