aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/splice.c22
-rw-r--r--fs/xattr.c20
-rw-r--r--include/linux/fs.h1
-rw-r--r--include/linux/net.h2
-rw-r--r--include/linux/skbuff.h3
-rw-r--r--include/linux/splice.h4
-rw-r--r--include/net/tcp.h2
-rw-r--r--kernel/relay.c1
-rw-r--r--kernel/trace/trace.c2
-rw-r--r--net/core/skbuff.c4
-rw-r--r--net/ipv4/tcp.c7
-rw-r--r--net/kcm/kcmsock.c5
-rw-r--r--net/smc/af_smc.c5
-rw-r--r--net/socket.c4
-rw-r--r--net/unix/af_unix.c113
15 files changed, 54 insertions, 141 deletions
diff --git a/fs/splice.c b/fs/splice.c
index 006ba50f4ece..9299b59511b9 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -247,11 +247,6 @@ ssize_t add_to_pipe(struct pipe_inode_info *pipe, struct pipe_buffer *buf)
}
EXPORT_SYMBOL(add_to_pipe);
-void spd_release_page(struct splice_pipe_desc *spd, unsigned int i)
-{
- put_page(spd->pages[i]);
-}
-
/*
* Check if we need to grow the arrays holding pages and partial page
* descriptions.
@@ -308,6 +303,8 @@ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos,
iov_iter_pipe(&to, ITER_PIPE | READ, pipe, len);
idx = to.idx;
init_sync_kiocb(&kiocb, in);
+ if (flags & SPLICE_F_NONBLOCK)
+ kiocb.ki_flags |= IOCB_NDELAY;
kiocb.ki_pos = *ppos;
ret = call_read_iter(in, &kiocb, &to);
if (ret > 0) {
@@ -393,7 +390,7 @@ static ssize_t default_file_splice_read(struct file *in, loff_t *ppos,
struct iov_iter to;
struct page **pages;
unsigned int nr_pages;
- size_t offset, dummy, copied = 0;
+ size_t offset, base, copied = 0;
ssize_t res;
int i;
@@ -408,12 +405,11 @@ static ssize_t default_file_splice_read(struct file *in, loff_t *ppos,
iov_iter_pipe(&to, ITER_PIPE | READ, pipe, len + offset);
- res = iov_iter_get_pages_alloc(&to, &pages, len + offset, &dummy);
+ res = iov_iter_get_pages_alloc(&to, &pages, len + offset, &base);
if (res <= 0)
return -ENOMEM;
- BUG_ON(dummy);
- nr_pages = DIV_ROUND_UP(res, PAGE_SIZE);
+ nr_pages = DIV_ROUND_UP(res + base, PAGE_SIZE);
vec = __vec;
if (nr_pages > PIPE_DEF_BUFFERS) {
@@ -1359,6 +1355,8 @@ SYSCALL_DEFINE4(vmsplice, int, fd, const struct iovec __user *, iov,
struct fd f;
long error;
+ if (unlikely(flags & ~SPLICE_F_ALL))
+ return -EINVAL;
if (unlikely(nr_segs > UIO_MAXIOV))
return -EINVAL;
else if (unlikely(!nr_segs))
@@ -1409,6 +1407,9 @@ SYSCALL_DEFINE6(splice, int, fd_in, loff_t __user *, off_in,
if (unlikely(!len))
return 0;
+ if (unlikely(flags & ~SPLICE_F_ALL))
+ return -EINVAL;
+
error = -EBADF;
in = fdget(fd_in);
if (in.file) {
@@ -1737,6 +1738,9 @@ SYSCALL_DEFINE4(tee, int, fdin, int, fdout, size_t, len, unsigned int, flags)
struct fd in;
int error;
+ if (unlikely(flags & ~SPLICE_F_ALL))
+ return -EINVAL;
+
if (unlikely(!len))
return 0;
diff --git a/fs/xattr.c b/fs/xattr.c
index 7e3317cf4045..c19a16323c2c 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -537,16 +537,18 @@ getxattr(struct dentry *d, const char __user *name, void __user *value,
}
error = vfs_getxattr(d, kname, kvalue, size);
+ if (error > XATTR_SIZE_MAX ||
+ (error == -ERANGE && size >= XATTR_SIZE_MAX)) {
+ /* The file system tried to returned a value bigger
+ than XATTR_SIZE_MAX bytes. Not possible. */
+ error = -E2BIG;
+ }
if (error > 0) {
if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
(strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
posix_acl_fix_xattr_to_user(kvalue, size);
if (size && copy_to_user(value, kvalue, error))
error = -EFAULT;
- } else if (error == -ERANGE && size >= XATTR_SIZE_MAX) {
- /* The file system tried to returned a value bigger
- than XATTR_SIZE_MAX bytes. Not possible. */
- error = -E2BIG;
}
kvfree(kvalue);
@@ -620,14 +622,16 @@ listxattr(struct dentry *d, char __user *list, size_t size)
}
error = vfs_listxattr(d, klist, size);
- if (error > 0) {
- if (size && copy_to_user(list, klist, error))
- error = -EFAULT;
- } else if (error == -ERANGE && size >= XATTR_LIST_MAX) {
+ if (error > XATTR_LIST_MAX ||
+ (error == -ERANGE && size >= XATTR_LIST_MAX)) {
/* The file system tried to returned a list bigger
than XATTR_LIST_MAX bytes. Not possible. */
error = -E2BIG;
}
+ if (error > 0) {
+ if (size && copy_to_user(list, klist, error))
+ error = -EFAULT;
+ }
kvfree(klist);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 7251f7bb45e8..0dc9bc3fb1df 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -270,6 +270,7 @@ struct writeback_control;
#define IOCB_DSYNC (1 << 4)
#define IOCB_SYNC (1 << 5)
#define IOCB_WRITE (1 << 6)
+#define IOCB_NDELAY (1 << 7)
struct kiocb {
struct file *ki_filp;
diff --git a/include/linux/net.h b/include/linux/net.h
index 0620f5e18c96..7b89e24de8a1 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -186,7 +186,7 @@ struct proto_ops {
struct vm_area_struct * vma);
ssize_t (*sendpage) (struct socket *sock, struct page *page,
int offset, size_t size, int flags);
- ssize_t (*splice_read)(struct socket *sock, loff_t *ppos,
+ ssize_t (*splice_read)(struct file *file, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len, unsigned int flags);
int (*set_peek_off)(struct sock *sk, int val);
int (*peek_len)(struct socket *sock);
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index c776abd86937..4bcb75ffbad0 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -3091,8 +3091,7 @@ int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len);
__wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to,
int len, __wsum csum);
int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset,
- struct pipe_inode_info *pipe, unsigned int len,
- unsigned int flags);
+ struct pipe_inode_info *pipe, unsigned int len);
void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to);
unsigned int skb_zerocopy_headlen(const struct sk_buff *from);
int skb_zerocopy(struct sk_buff *to, struct sk_buff *from,
diff --git a/include/linux/splice.h b/include/linux/splice.h
index 00a21166e268..db42746bdfea 100644
--- a/include/linux/splice.h
+++ b/include/linux/splice.h
@@ -20,6 +20,8 @@
#define SPLICE_F_MORE (0x04) /* expect more data */
#define SPLICE_F_GIFT (0x08) /* pages passed in are a gift */
+#define SPLICE_F_ALL (SPLICE_F_MOVE|SPLICE_F_NONBLOCK|SPLICE_F_MORE|SPLICE_F_GIFT)
+
/*
* Passed to the actors
*/
@@ -55,7 +57,6 @@ struct splice_pipe_desc {
struct partial_page *partial; /* pages[] may not be contig */
int nr_pages; /* number of populated pages in map */
unsigned int nr_pages_max; /* pages[] & partial[] arrays size */
- unsigned int flags; /* splice flags */
const struct pipe_buf_operations *ops;/* ops associated with output pipe */
void (*spd_release)(struct splice_pipe_desc *, unsigned int);
};
@@ -82,7 +83,6 @@ extern ssize_t splice_direct_to_actor(struct file *, struct splice_desc *,
*/
extern int splice_grow_spd(const struct pipe_inode_info *, struct splice_pipe_desc *);
extern void splice_shrink_spd(struct splice_pipe_desc *);
-extern void spd_release_page(struct splice_pipe_desc *, unsigned int);
extern const struct pipe_buf_operations page_cache_pipe_buf_ops;
extern const struct pipe_buf_operations default_pipe_buf_ops;
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 6ec4ea652f3f..1fce46b3d839 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -361,7 +361,7 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
void tcp_rcv_space_adjust(struct sock *sk);
int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp);
void tcp_twsk_destructor(struct sock *sk);
-ssize_t tcp_splice_read(struct socket *sk, loff_t *ppos,
+ssize_t tcp_splice_read(struct file *file, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len,
unsigned int flags);
diff --git a/kernel/relay.c b/kernel/relay.c
index 0e413d9eec8a..39a9dfc69486 100644
--- a/kernel/relay.c
+++ b/kernel/relay.c
@@ -1212,7 +1212,6 @@ static ssize_t subbuf_splice_actor(struct file *in,
.nr_pages = 0,
.nr_pages_max = PIPE_DEF_BUFFERS,
.partial = partial,
- .flags = flags,
.ops = &relay_pipe_buf_ops,
.spd_release = relay_page_release,
};
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index f35109514a01..12f67868358b 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -5529,7 +5529,6 @@ static ssize_t tracing_splice_read_pipe(struct file *filp,
.partial = partial_def,
.nr_pages = 0, /* This gets updated below. */
.nr_pages_max = PIPE_DEF_BUFFERS,
- .flags = flags,
.ops = &tracing_pipe_buf_ops,
.spd_release = tracing_spd_release_pipe,
};
@@ -6427,7 +6426,6 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos,
.pages = pages_def,
.partial = partial_def,
.nr_pages_max = PIPE_DEF_BUFFERS,
- .flags = flags,
.ops = &buffer_pipe_buf_ops,
.spd_release = buffer_spd_release,
};
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 9f781092fda9..f30d65ba2eb8 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -1971,8 +1971,7 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
* the fragments, and the frag list.
*/
int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset,
- struct pipe_inode_info *pipe, unsigned int tlen,
- unsigned int flags)
+ struct pipe_inode_info *pipe, unsigned int tlen)
{
struct partial_page partial[MAX_SKB_FRAGS];
struct page *pages[MAX_SKB_FRAGS];
@@ -1980,7 +1979,6 @@ int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset,
.pages = pages,
.partial = partial,
.nr_pages_max = MAX_SKB_FRAGS,
- .flags = flags,
.ops = &nosteal_pipe_buf_ops,
.spd_release = sock_spd_release,
};
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 1e319a525d51..9b1b1c09d622 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -311,7 +311,6 @@ EXPORT_SYMBOL(tcp_sockets_allocated);
struct tcp_splice_state {
struct pipe_inode_info *pipe;
size_t len;
- unsigned int flags;
};
/*
@@ -690,7 +689,7 @@ static int tcp_splice_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
int ret;
ret = skb_splice_bits(skb, skb->sk, offset, tss->pipe,
- min(rd_desc->count, len), tss->flags);
+ min(rd_desc->count, len));
if (ret > 0)
rd_desc->count -= ret;
return ret;
@@ -719,15 +718,15 @@ static int __tcp_splice_read(struct sock *sk, struct tcp_splice_state *tss)
* Will read pages from given socket and fill them into a pipe.
*
**/
-ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
+ssize_t tcp_splice_read(struct file *file, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len,
unsigned int flags)
{
+ struct socket *sock = file->private_data;
struct sock *sk = sock->sk;
struct tcp_splice_state tss = {
.pipe = pipe,
.len = len,
- .flags = flags,
};
long timeo;
ssize_t spliced;
diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c
index 309062f3debe..876da9dfd3fa 100644
--- a/net/kcm/kcmsock.c
+++ b/net/kcm/kcmsock.c
@@ -1168,10 +1168,11 @@ out:
return copied ? : err;
}
-static ssize_t kcm_splice_read(struct socket *sock, loff_t *ppos,
+static ssize_t kcm_splice_read(struct file *file, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len,
unsigned int flags)
{
+ struct socket *sock = file->private_data;
struct sock *sk = sock->sk;
struct kcm_sock *kcm = kcm_sk(sk);
long timeo;
@@ -1197,7 +1198,7 @@ static ssize_t kcm_splice_read(struct socket *sock, loff_t *ppos,
if (len > rxm->full_len)
len = rxm->full_len;
- copied = skb_splice_bits(skb, sk, rxm->offset, pipe, len, flags);
+ copied = skb_splice_bits(skb, sk, rxm->offset, pipe, len);
if (copied < 0) {
err = copied;
goto err_out;
diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c
index 093803786eac..7a98dda6d1e0 100644
--- a/net/smc/af_smc.c
+++ b/net/smc/af_smc.c
@@ -1244,10 +1244,11 @@ out:
return rc;
}
-static ssize_t smc_splice_read(struct socket *sock, loff_t *ppos,
+static ssize_t smc_splice_read(struct file *file, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len,
unsigned int flags)
{
+ struct socket *sock = file->private_data;
struct sock *sk = sock->sk;
struct smc_sock *smc;
int rc = -ENOTCONN;
@@ -1257,7 +1258,7 @@ static ssize_t smc_splice_read(struct socket *sock, loff_t *ppos,
if ((sk->sk_state != SMC_ACTIVE) && (sk->sk_state != SMC_CLOSED))
goto out;
if (smc->use_fallback) {
- rc = smc->clcsock->ops->splice_read(smc->clcsock, ppos,
+ rc = smc->clcsock->ops->splice_read(file, ppos,
pipe, len, flags);
} else {
rc = -EOPNOTSUPP;
diff --git a/net/socket.c b/net/socket.c
index 985ef06792d6..62db2bbebe5b 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -812,7 +812,7 @@ static ssize_t sock_splice_read(struct file *file, loff_t *ppos,
if (unlikely(!sock->ops->splice_read))
return -EINVAL;
- return sock->ops->splice_read(sock, ppos, pipe, len, flags);
+ return sock->ops->splice_read(file, ppos, pipe, len, flags);
}
static ssize_t sock_read_iter(struct kiocb *iocb, struct iov_iter *to)
@@ -823,7 +823,7 @@ static ssize_t sock_read_iter(struct kiocb *iocb, struct iov_iter *to)
.msg_iocb = iocb};
ssize_t res;
- if (file->f_flags & O_NONBLOCK)
+ if ((file->f_flags & O_NONBLOCK) || (iocb->ki_flags & IOCB_NDELAY))
msg.msg_flags = MSG_DONTWAIT;
if (iocb->ki_pos != 0)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 928691c43408..26aaa5fd841b 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -647,9 +647,6 @@ static int unix_stream_sendmsg(struct socket *, struct msghdr *, size_t);
static int unix_stream_recvmsg(struct socket *, struct msghdr *, size_t, int);
static ssize_t unix_stream_sendpage(struct socket *, struct page *, int offset,
size_t size, int flags);
-static ssize_t unix_stream_splice_read(struct socket *, loff_t *ppos,
- struct pipe_inode_info *, size_t size,
- unsigned int flags);
static int unix_dgram_sendmsg(struct socket *, struct msghdr *, size_t);
static int unix_dgram_recvmsg(struct socket *, struct msghdr *, size_t, int);
static int unix_dgram_connect(struct socket *, struct sockaddr *,
@@ -691,7 +688,7 @@ static const struct proto_ops unix_stream_ops = {
.recvmsg = unix_stream_recvmsg,
.mmap = sock_no_mmap,
.sendpage = unix_stream_sendpage,
- .splice_read = unix_stream_splice_read,
+ .splice_read = generic_file_splice_read,
.set_peek_off = unix_set_peek_off,
};
@@ -2250,34 +2247,21 @@ static unsigned int unix_skb_len(const struct sk_buff *skb)
return skb->len - UNIXCB(skb).consumed;
}
-struct unix_stream_read_state {
- int (*recv_actor)(struct sk_buff *, int, int,
- struct unix_stream_read_state *);
- struct socket *socket;
- struct msghdr *msg;
- struct pipe_inode_info *pipe;
- size_t size;
- int flags;
- unsigned int splice_flags;
-};
-
-static int unix_stream_read_generic(struct unix_stream_read_state *state,
- bool freezable)
+static int unix_stream_recvmsg(struct socket *sock, struct msghdr *msg,
+ size_t size, int flags)
{
struct scm_cookie scm;
- struct socket *sock = state->socket;
struct sock *sk = sock->sk;
struct unix_sock *u = unix_sk(sk);
int copied = 0;
- int flags = state->flags;
int noblock = flags & MSG_DONTWAIT;
bool check_creds = false;
int target;
int err = 0;
long timeo;
int skip;
- size_t size = state->size;
unsigned int last_len;
+ bool freezable = !(msg->msg_iter.type & ITER_PIPE);
if (unlikely(sk->sk_state != TCP_ESTABLISHED)) {
err = -EINVAL;
@@ -2306,7 +2290,6 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
do {
int chunk;
- bool drop_skb;
struct sk_buff *skb, *last;
redo:
@@ -2380,20 +2363,16 @@ unlock:
}
/* Copy address just once */
- if (state->msg && state->msg->msg_name) {
+ if (msg->msg_name) {
DECLARE_SOCKADDR(struct sockaddr_un *, sunaddr,
- state->msg->msg_name);
- unix_copy_addr(state->msg, skb->sk);
+ msg->msg_name);
+ unix_copy_addr(msg, skb->sk);
sunaddr = NULL;
}
chunk = min_t(unsigned int, unix_skb_len(skb) - skip, size);
- skb_get(skb);
- chunk = state->recv_actor(skb, skip, chunk, state);
- drop_skb = !unix_skb_len(skb);
- /* skb is only safe to use if !drop_skb */
- consume_skb(skb);
- if (chunk < 0) {
+ if (skb_copy_datagram_msg(skb, UNIXCB(skb).consumed + skip,
+ msg, chunk)) {
if (copied == 0)
copied = -EFAULT;
break;
@@ -2401,18 +2380,6 @@ unlock:
copied += chunk;
size -= chunk;
- if (drop_skb) {
- /* the skb was touched by a concurrent reader;
- * we should not expect anything from this skb
- * anymore and assume it invalid - we can be
- * sure it was dropped from the socket queue
- *
- * let's report a short read
- */
- err = 0;
- break;
- }
-
/* Mark read part of skb as used */
if (!(flags & MSG_PEEK)) {
UNIXCB(skb).consumed += chunk;
@@ -2454,70 +2421,12 @@ unlock:
} while (size);
mutex_unlock(&u->iolock);
- if (state->msg)
- scm_recv(sock, state->msg, &scm, flags);
- else
- scm_destroy(&scm);
+ if (msg)
+ scm_recv(sock, msg, &scm, flags);
out:
return copied ? : err;
}
-static int unix_stream_read_actor(struct sk_buff *skb,
- int skip, int chunk,
- struct unix_stream_read_state *state)
-{
- int ret;
-
- ret = skb_copy_datagram_msg(skb, UNIXCB(skb).consumed + skip,
- state->msg, chunk);
- return ret ?: chunk;
-}
-
-static int unix_stream_recvmsg(struct socket *sock, struct msghdr *msg,
- size_t size, int flags)
-{
- struct unix_stream_read_state state = {
- .recv_actor = unix_stream_read_actor,
- .socket = sock,
- .msg = msg,
- .size = size,
- .flags = flags
- };
-
- return unix_stream_read_generic(&state, true);
-}
-
-static int unix_stream_splice_actor(struct sk_buff *skb,
- int skip, int chunk,
- struct unix_stream_read_state *state)
-{
- return skb_splice_bits(skb, state->socket->sk,
- UNIXCB(skb).consumed + skip,
- state->pipe, chunk, state->splice_flags);
-}
-
-static ssize_t unix_stream_splice_read(struct socket *sock, loff_t *ppos,
- struct pipe_inode_info *pipe,
- size_t size, unsigned int flags)
-{
- struct unix_stream_read_state state = {
- .recv_actor = unix_stream_splice_actor,
- .socket = sock,
- .pipe = pipe,
- .size = size,
- .splice_flags = flags,
- };
-
- if (unlikely(*ppos))
- return -ESPIPE;
-
- if (sock->file->f_flags & O_NONBLOCK ||
- flags & SPLICE_F_NONBLOCK)
- state.flags = MSG_DONTWAIT;
-
- return unix_stream_read_generic(&state, false);
-}
-
static int unix_shutdown(struct socket *sock, int mode)
{
struct sock *sk = sock->sk;