Commit 2bfc6685 authored by David Howells's avatar David Howells Committed by Jakub Kicinski

splice, net: Add a splice_eof op to file-ops and socket-ops

Add an optional method, ->splice_eof(), to allow splice to indicate the
premature termination of a splice to struct file_operations and struct
proto_ops.

This is called if sendfile() or splice() encounters all of the following
conditions inside splice_direct_to_actor():

 (1) the user did not set SPLICE_F_MORE (splice only), and

 (2) an EOF condition occurred (->splice_read() returned 0), and

 (3) we haven't read enough to fulfill the request (ie. len > 0 still), and

 (4) we have already spliced at least one byte.

A further patch will modify the behaviour of SPLICE_F_MORE to always be
passed to the actor if either the user set it or we haven't yet read
sufficient data to fulfill the request.
Suggested-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Link: https://lore.kernel.org/r/CAHk-=wh=V579PDYvkpnTobCLGczbgxpMgGmmhqiTyE34Cpi5Gg@mail.gmail.com/Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Reviewed-by: default avatarJakub Kicinski <kuba@kernel.org>
cc: Jens Axboe <axboe@kernel.dk>
cc: Christoph Hellwig <hch@lst.de>
cc: Al Viro <viro@zeniv.linux.org.uk>
cc: Matthew Wilcox <willy@infradead.org>
cc: Jan Kara <jack@suse.cz>
cc: Jeff Layton <jlayton@kernel.org>
cc: David Hildenbrand <david@redhat.com>
cc: Christian Brauner <brauner@kernel.org>
cc: Chuck Lever <chuck.lever@oracle.com>
cc: Boris Pismenny <borisp@nvidia.com>
cc: John Fastabend <john.fastabend@gmail.com>
cc: linux-mm@kvack.org
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 2dc334f1
...@@ -969,6 +969,17 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ...@@ -969,6 +969,17 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
return out->f_op->splice_write(pipe, out, ppos, len, flags); return out->f_op->splice_write(pipe, out, ppos, len, flags);
} }
/*
* Indicate to the caller that there was a premature EOF when reading from the
* source and the caller didn't indicate they would be sending more data after
* this.
*/
static void do_splice_eof(struct splice_desc *sd)
{
if (sd->splice_eof)
sd->splice_eof(sd);
}
/* /*
* Attempt to initiate a splice from a file to a pipe. * Attempt to initiate a splice from a file to a pipe.
*/ */
...@@ -1068,7 +1079,7 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd, ...@@ -1068,7 +1079,7 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
ret = do_splice_to(in, &pos, pipe, len, flags); ret = do_splice_to(in, &pos, pipe, len, flags);
if (unlikely(ret <= 0)) if (unlikely(ret <= 0))
goto out_release; goto read_failure;
read_len = ret; read_len = ret;
sd->total_len = read_len; sd->total_len = read_len;
...@@ -1108,6 +1119,15 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd, ...@@ -1108,6 +1119,15 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
file_accessed(in); file_accessed(in);
return bytes; return bytes;
read_failure:
/*
* If the user did *not* set SPLICE_F_MORE *and* we didn't hit that
* "use all of len" case that cleared SPLICE_F_MORE, *and* we did a
* "->splice_in()" that returned EOF (ie zero) *and* we have sent at
* least 1 byte *then* we will also do the ->splice_eof() call.
*/
if (ret == 0 && !more && len > 0 && bytes)
do_splice_eof(sd);
out_release: out_release:
/* /*
* If we did an incomplete transfer we must release * If we did an incomplete transfer we must release
...@@ -1136,6 +1156,14 @@ static int direct_splice_actor(struct pipe_inode_info *pipe, ...@@ -1136,6 +1156,14 @@ static int direct_splice_actor(struct pipe_inode_info *pipe,
sd->flags); sd->flags);
} }
static void direct_file_splice_eof(struct splice_desc *sd)
{
struct file *file = sd->u.file;
if (file->f_op->splice_eof)
file->f_op->splice_eof(file);
}
/** /**
* do_splice_direct - splices data directly between two files * do_splice_direct - splices data directly between two files
* @in: file to splice from * @in: file to splice from
...@@ -1161,6 +1189,7 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, ...@@ -1161,6 +1189,7 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
.flags = flags, .flags = flags,
.pos = *ppos, .pos = *ppos,
.u.file = out, .u.file = out,
.splice_eof = direct_file_splice_eof,
.opos = opos, .opos = opos,
}; };
long ret; long ret;
......
...@@ -1796,6 +1796,7 @@ struct file_operations { ...@@ -1796,6 +1796,7 @@ struct file_operations {
int (*flock) (struct file *, int, struct file_lock *); int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
void (*splice_eof)(struct file *file);
int (*setlease)(struct file *, long, struct file_lock **, void **); int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset, long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len); loff_t len);
......
...@@ -210,6 +210,7 @@ struct proto_ops { ...@@ -210,6 +210,7 @@ struct proto_ops {
int offset, size_t size, int flags); int offset, size_t size, int flags);
ssize_t (*splice_read)(struct socket *sock, loff_t *ppos, ssize_t (*splice_read)(struct socket *sock, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len, unsigned int flags); struct pipe_inode_info *pipe, size_t len, unsigned int flags);
void (*splice_eof)(struct socket *sock);
int (*set_peek_off)(struct sock *sk, int val); int (*set_peek_off)(struct sock *sk, int val);
int (*peek_len)(struct socket *sock); int (*peek_len)(struct socket *sock);
......
...@@ -38,6 +38,7 @@ struct splice_desc { ...@@ -38,6 +38,7 @@ struct splice_desc {
struct file *file; /* file to read/write */ struct file *file; /* file to read/write */
void *data; /* cookie */ void *data; /* cookie */
} u; } u;
void (*splice_eof)(struct splice_desc *sd); /* Unexpected EOF handler */
loff_t pos; /* file position */ loff_t pos; /* file position */
loff_t *opos; /* sendfile: output position */ loff_t *opos; /* sendfile: output position */
size_t num_spliced; /* number of bytes already spliced */ size_t num_spliced; /* number of bytes already spliced */
......
...@@ -1279,6 +1279,7 @@ struct proto { ...@@ -1279,6 +1279,7 @@ struct proto {
size_t len, int flags, int *addr_len); size_t len, int flags, int *addr_len);
int (*sendpage)(struct sock *sk, struct page *page, int (*sendpage)(struct sock *sk, struct page *page,
int offset, size_t size, int flags); int offset, size_t size, int flags);
void (*splice_eof)(struct socket *sock);
int (*bind)(struct sock *sk, int (*bind)(struct sock *sk,
struct sockaddr *addr, int addr_len); struct sockaddr *addr, int addr_len);
int (*bind_add)(struct sock *sk, int (*bind_add)(struct sock *sk,
......
...@@ -130,6 +130,7 @@ static int sock_fasync(int fd, struct file *filp, int on); ...@@ -130,6 +130,7 @@ static int sock_fasync(int fd, struct file *filp, int on);
static ssize_t sock_splice_read(struct file *file, loff_t *ppos, static ssize_t sock_splice_read(struct file *file, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len, struct pipe_inode_info *pipe, size_t len,
unsigned int flags); unsigned int flags);
static void sock_splice_eof(struct file *file);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
static void sock_show_fdinfo(struct seq_file *m, struct file *f) static void sock_show_fdinfo(struct seq_file *m, struct file *f)
...@@ -163,6 +164,7 @@ static const struct file_operations socket_file_ops = { ...@@ -163,6 +164,7 @@ static const struct file_operations socket_file_ops = {
.fasync = sock_fasync, .fasync = sock_fasync,
.splice_write = splice_to_socket, .splice_write = splice_to_socket,
.splice_read = sock_splice_read, .splice_read = sock_splice_read,
.splice_eof = sock_splice_eof,
.show_fdinfo = sock_show_fdinfo, .show_fdinfo = sock_show_fdinfo,
}; };
...@@ -1076,6 +1078,14 @@ static ssize_t sock_splice_read(struct file *file, loff_t *ppos, ...@@ -1076,6 +1078,14 @@ static ssize_t sock_splice_read(struct file *file, loff_t *ppos,
return sock->ops->splice_read(sock, ppos, pipe, len, flags); return sock->ops->splice_read(sock, ppos, pipe, len, flags);
} }
static void sock_splice_eof(struct file *file)
{
struct socket *sock = file->private_data;
if (sock->ops->splice_eof)
sock->ops->splice_eof(sock);
}
static ssize_t sock_read_iter(struct kiocb *iocb, struct iov_iter *to) static ssize_t sock_read_iter(struct kiocb *iocb, struct iov_iter *to)
{ {
struct file *file = iocb->ki_filp; struct file *file = iocb->ki_filp;
......
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