aboutsummaryrefslogtreecommitdiff
path: root/fs/splice.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/splice.c')
-rw-r--r--fs/splice.c255
1 files changed, 201 insertions, 54 deletions
diff --git a/fs/splice.c b/fs/splice.c
index 4dc75c96334..0d810e86f6c 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -58,6 +58,21 @@ static void put_splice_pipe(struct pipe_inode_info *pipe);
//#define SPLICE_NO_IO 1
+//#define SPLICE_CHECK_SIGNAL_PENDING
+#define SPLICE_READ_ALL
+#define SPLICE_FAST
+
+static inline int ignore_splice_error ( long ret )
+{
+ switch ( ret )
+ {
+ case -EAGAIN :
+ case -ERESTARTSYS :
+ case -EINTR : return 1;
+ default : return 0;
+ }
+}
+
/*
* 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
@@ -214,10 +229,12 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
for (;;) {
if (!pipe->readers) {
send_sig(SIGPIPE, current, 0);
- if (!ret)
+ if (!ret) {
+ printk(KERN_ERR "%s:%s:%d\n"
+ "returning EPIPE\n", __FUNCTION__, __FILE__, __LINE__);
ret = -EPIPE;
+ }
break;
- printk("%s:%d No readers breaking\n", __FUNCTION__, __LINE__);
}
@@ -251,13 +268,16 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
}
if (spd->flags & SPLICE_F_NONBLOCK) {
- if (!ret)
+ if (!ret) {
+ printk(KERN_ERR "%s:%s:%d\n"
+ "returning EAGAIN\n", __FUNCTION__, __FILE__, __LINE__);
ret = -EAGAIN;
+ }
break;
}
- if (signal_pending(current)) {
+ if (signal_pending(current)) { // exists in distro-kernel
if (!ret)
ret = -ERESTARTSYS;
@@ -882,11 +902,6 @@ ssize_t __splice_from_pipe_special(struct pipe_inode_info *pipe, struct splice_d
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);
@@ -925,8 +940,11 @@ ssize_t __splice_from_pipe_special(struct pipe_inode_info *pipe, struct splice_d
AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata);
- if (unlikely(ret))
+ if (unlikely(ret)) {
+ printk(KERN_ERR "%s:%s:%d\n"
+ "returning err %d\n", __FUNCTION__, __FILE__, __LINE__, ret);
goto out;
+ }
char *dst = kmap(page);
while(not_copied) {
@@ -997,8 +1015,11 @@ ssize_t __splice_from_pipe_special(struct pipe_inode_info *pipe, struct splice_d
break;
if (sd->flags & SPLICE_F_NONBLOCK) {
- if (!retval)
+ if (!retval) {
+ printk(KERN_ERR "%s:%s:%d\n"
+ "returning EAGAIN\n", __FUNCTION__, __FILE__, __LINE__);
retval = -EAGAIN;
+ }
break;
}
}
@@ -1044,20 +1065,19 @@ ssize_t __splice_from_pipe_dma(struct pipe_inode_info *pipe, struct splice_desc
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;
+ printk(KERN_ERR "%s:%s:%d\n"
+ "returning ENOMEM\n", __FUNCTION__, __FILE__, __LINE__);
goto cleanup;
}
pipe_addr_map = kzalloc(sizeof(*pipe_addr_map) * PIPE_BUFFERS, GFP_ATOMIC);
if(!pipe_addr_map) {
retval = -ENOMEM;
+ printk(KERN_ERR "%s:%s:%d\n"
+ "returning ENOMEM\n", __FUNCTION__, __FILE__, __LINE__);
goto cleanup;
}
@@ -1083,6 +1103,9 @@ ssize_t __splice_from_pipe_dma(struct pipe_inode_info *pipe, struct splice_desc
if (unlikely(ret)) {
retval = ret;
+ printk(KERN_ERR "%s:%s:%d\n"
+ "returning err %d\n",
+ __FUNCTION__, __FILE__, __LINE__, ret);
goto cleanup;
}
@@ -1105,6 +1128,9 @@ ssize_t __splice_from_pipe_dma(struct pipe_inode_info *pipe, struct splice_desc
ret = buf->ops->confirm(pipe, buf);
if (unlikely(ret)) {
retval = ret;
+ printk(KERN_ERR "%s:%s:%d\n"
+ "returning err %d\n",
+ __FUNCTION__, __FILE__, __LINE__, ret);
goto cleanup;
}
#endif
@@ -1173,7 +1199,8 @@ ssize_t __splice_from_pipe_dma(struct pipe_inode_info *pipe, struct splice_desc
mutex_unlock(&splice_dma_lock);
if(ret) {
- printk("%s:%s:%d - splice_dma_memcpy failed with %d\n",
+ printk("%s:%s:%d\n"
+ "splice_dma_memcpy failed with %d\n",
__FILE__, __FUNCTION__, __LINE__, ret);
kunmap(page);
pagecache_write_end(file, mapping, page_pos, 0, 0,
@@ -1205,8 +1232,11 @@ ssize_t __splice_from_pipe_dma(struct pipe_inode_info *pipe, struct splice_desc
if (sd->flags & SPLICE_F_NONBLOCK) {
- if (!retval)
+ if (!retval) {
+ printk(KERN_ERR "%s:%s:%d\n"
+ "returning EAGAIN\n", __FUNCTION__, __FILE__, __LINE__);
retval = -EAGAIN;
+ }
break;
}
}
@@ -1644,11 +1674,6 @@ ssize_t splice_from_pipe_special(struct pipe_inode_info *pipe, struct file *out,
.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
@@ -1687,7 +1712,7 @@ int splice_from_pipe_next(struct pipe_inode_info *pipe, struct splice_desc *sd)
if (sd->flags & SPLICE_F_NONBLOCK)
return -EAGAIN;
- if (signal_pending(current))
+ if (signal_pending(current)) // exists in distro-kernel
return -ERESTARTSYS;
if (sd->need_wakeup) {
@@ -1820,14 +1845,15 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
ssize_t ret;
int err = 0;
- if (signal_pending(current)) {
- release_splice_pipebufs_special(pipe);
- return -ERESTARTSYS;
- }
-
err = file_remove_suid(out);
- if(err)
+ if(err) {
+ release_splice_pipebufs_special(pipe);
+ printk(KERN_ERR "%s:%s:%d\n"
+ "returning err %d.\n",
+ __FUNCTION__, __FILE__, __LINE__,
+ err);
return err;
+ }
file_update_time(out);
mutex_lock(&inode->i_mutex);
@@ -1920,11 +1946,6 @@ 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;
@@ -2451,9 +2472,14 @@ static long do_splice_2(int fd_in, struct file *in, loff_t __user *off_in,
struct pipe_inode_info *pipe;
loff_t offset, *off;
long ret;
+ size_t spliced_len;
- if (signal_pending(current))
- return -ERESTARTSYS;
+ if (signal_pending(current)) {
+ printk(KERN_ERR "%s:%s:%d\n"
+ "signal pending returning EINTR.\n",
+ __FUNCTION__, __FILE__, __LINE__);
+ return -EINTR;
+ }
#ifdef DEBUG_SPLICE
printk("%s:%s:%d - len=%d\n", __FILE__, __FUNCTION__, __LINE__, len);
@@ -2462,8 +2488,9 @@ static long do_splice_2(int fd_in, struct file *in, loff_t __user *off_in,
#ifndef SPLICE_DEFER_DMA
pipe = alloc_pipe_info(NULL);
if (!pipe) {
- printk("%s %s:%d alloc_pipe_info failed\n",
- __FUNCTION__, __FILE__, __LINE__);
+ printk( KERN_ERR "%s %s:%d\n"
+ "alloc_pipe_info failed.\n",
+ __FUNCTION__, __FILE__, __LINE__);
return -ENOMEM;
}
@@ -2477,28 +2504,35 @@ static long do_splice_2(int fd_in, struct file *in, loff_t __user *off_in,
#else
pipe = get_splice_pipe();
- if(!pipe)
+ if(!pipe) {
+ printk(KERN_ERR "%s:%s:%d\n"
+ "returning ENOMEM.\n",
+ __FUNCTION__, __FILE__, __LINE__);
return -ENOMEM;
+ }
pipe->readers = 1;
#endif
if (off_in) {
- printk(KERN_ERR "%s:%s:%d off_in is seekable\n",
+ printk(KERN_ERR "%s:%s:%d\n"
+ "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",
+ printk(KERN_ERR "%s:%s:%d\n"
+ "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__);
+ printk(KERN_ERR "%s:%s:%d\n"
+ "copy_from_user failed.\n",
+ __FUNCTION__, __FILE__, __LINE__);
ret = -EFAULT;
goto out;
}
@@ -2511,6 +2545,8 @@ static long do_splice_2(int fd_in, struct file *in, loff_t __user *off_in,
printk("%s:%s:%d - calling in->f_op.splice_read len=%d\n",
__FILE__, __FUNCTION__, __LINE__, len);
#endif
+
+#ifndef SPLICE_READ_ALL
ret = in->f_op->splice_read(in, off_in, pipe, len, flags);
#ifdef DEBUG_SPLICE
@@ -2518,7 +2554,8 @@ static long do_splice_2(int fd_in, struct file *in, loff_t __user *off_in,
#endif
if(!ret) {
- printk(KERN_ERR "%s:%s:%d sock_splice_read read nothing\n",
+ printk(KERN_ERR "%s:%s:%d\n"
+ "sock_splice_read read nothing.\n",
__FILE__, __FUNCTION__, __LINE__);
ret = -EAGAIN;
goto out;
@@ -2526,22 +2563,119 @@ static long do_splice_2(int fd_in, struct file *in, loff_t __user *off_in,
#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);
+ if ( ret != len )
+ {
+ printk( KERN_ERR "%s:%s:%d\n"
+ "splice read %ld instead of %d, offset is %d.\n",
+ __FILE__, __FUNCTION__, __LINE__,
+ ret, len, (off == NULL) ? 0 : *off );
+ }
+
+ /** save the returned data length */
+ spliced_len = ret;
+
+#else // SPLICE_READ_ALL
+ {
+ size_t data_len;
+ int attempt;
+
+ /** init */
+ data_len = len ;
+ spliced_len = 0 ;
+ attempt = 0 ;
+
+ /** read data until expected length is received */
+ while ( data_len )
+ {
+ attempt++;
+
+ /** read data (calls tcp_splice_read() in tcp.c) */
+ ret = in->f_op->splice_read( in, off_in, pipe, data_len, flags );
+
+ /** handle error status */
+ if( ret <= 0 )
+ {
+ printk( KERN_ERR "%s:%s:%d\n"
+ "sock_splice_read read error %ld.\n",
+ __FILE__, __FUNCTION__, __LINE__,
+ ret );
+
+ /** fail on specific errors */
+ if ( ret == 0 || ! ignore_splice_error ( ret ) )
+ {
+ printk( KERN_ERR "%s:%s:%d\n"
+ "returning read error %ld "
+ "after reading %d out of %d bytes.\n",
+ __FILE__, __FUNCTION__, __LINE__,
+ ret, spliced_len, len );
+
+ release_splice_pipebufs_special(pipe);
+ goto out;
+ }
+ }
+
+ /** compute the balance data to be read in next iteration */
+ if ( ret > 0 )
+ {
+ data_len -= ret;
+ spliced_len += ret;
+ }
+
+ /** warn if attempting more than 1 splice read */
+ if ( data_len != 0 )
+ {
+ printk( KERN_ERR "%s:%s:%d\n"
+ "Warning: Could not read all the data on "
+ "attempt# %d. Read: %ld, Total Read: %d"
+ " ... trying again.\n",
+ __FILE__, __FUNCTION__, __LINE__,
+ attempt, ret, spliced_len );
+ }
+ }
+
+ /** warn if more data than expected is read */
+ // NB: Should not happen
+ if ( spliced_len != len )
+ {
+ printk( KERN_ERR "%s:%s:%d\n"
+ "Should Not Happen. "
+ "splice read %ld instead of %d, offset is %lld.\n",
+ __FILE__, __FUNCTION__, __LINE__,
+ ret, len, (off == NULL) ? 0 : *off );
+ }
+
+ }
+#endif // SPLICE_READ_ALL
+
+ ret = do_splice_from(pipe, out, off, spliced_len, flags);
#ifdef DEBUG_SPLICE
printk("%s:%s:%d do_splice_from returns %d\n", __FILE__, __FUNCTION__, __LINE__,
ret);
#endif
+ /** handle errors */
+ if ( ret < 0 )
+ {
+ release_splice_pipebufs_special(pipe);
+ goto out;
+ }
+
+ if ( ret != spliced_len )
+ {
+ printk( KERN_ERR "%s:%s:%d\n"
+ "splice wrote %ld instead of %d\n",
+ __FILE__, __FUNCTION__, __LINE__,
+ ret, spliced_len );
+ }
+
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__);
+ __FILE__, __FUNCTION__, __LINE__);
ret = -EFAULT;
}
@@ -2551,6 +2685,15 @@ static long do_splice_2(int fd_in, struct file *in, loff_t __user *off_in,
out:
kfree(pipe);
+
+ if ( ret < 0 )
+ {
+ printk( KERN_ERR "%s:%s:%d\n"
+ "returning ret=%ld\n",
+ __FILE__, __FUNCTION__, __LINE__,
+ ret );
+ }
+
return ret;
}
/*
@@ -2888,8 +3031,12 @@ SYSCALL_DEFINE6(splice, int, fd_in, loff_t __user *, off_in,
if (unlikely(!len))
return 0;
- if (signal_pending(current))
- return -ERESTARTSYS;
+ if (signal_pending(current)) {
+ printk(KERN_ERR "%s:%s:%d\n"
+ "signal pending returning EINTR.\n",
+ __FUNCTION__, __FILE__, __LINE__);
+ return -EINTR;
+ }
in = fget_light(fd_in, &fput_in);
if (in) {
@@ -2897,7 +3044,7 @@ SYSCALL_DEFINE6(splice, int, fd_in, loff_t __user *, off_in,
out = fget_light(fd_out, &fput_out);
if (out) {
if (out->f_mode & FMODE_WRITE)
-#if 0
+#ifndef SPLICE_FAST
error = do_splice(in, off_in,
out, off_out,
len, flags);
@@ -2937,7 +3084,7 @@ static int ipipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
pipe_lock(pipe);
while (!pipe->nrbufs) {
- if (signal_pending(current)) {
+ if (signal_pending(current)) { // exists in distro-kernel
ret = -ERESTARTSYS;
break;
}
@@ -2984,7 +3131,7 @@ static int opipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
ret = -EAGAIN;
break;
}
- if (signal_pending(current)) {
+ if (signal_pending(current)) { // exists in distro-kernel
ret = -ERESTARTSYS;
break;
}