]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
splice, net: Add a splice_eof op to file-ops and socket-ops
authorDavid Howells <dhowells@redhat.com>
Wed, 7 Jun 2023 18:19:10 +0000 (19:19 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 29 Oct 2025 13:03:08 +0000 (14:03 +0100)
[ Upstream commit 2bfc66850952b6921b2033b09729ec59eabbc81d ]

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: Linus Torvalds <torvalds@linux-foundation.org>
Link: https://lore.kernel.org/r/CAHk-=wh=V579PDYvkpnTobCLGczbgxpMgGmmhqiTyE34Cpi5Gg@mail.gmail.com/
Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Jakub 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: Jakub Kicinski <kuba@kernel.org>
Stable-dep-of: b014a4e066c5 ("tls: wait for async encrypt in case of error during latter iterations of sendmsg")
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/splice.c
include/linux/fs.h
include/linux/net.h
include/linux/splice.h
include/net/sock.h
net/socket.c

index 5dbce4dcc1a7df61559eb19b0ba3c471afc1debe..e8591211044a39dce45d92886aa0d6c162ebda39 100644 (file)
@@ -767,6 +767,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);
 }
 
+/*
+ * 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.
  */
@@ -869,7 +880,7 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
 
                ret = do_splice_to(in, &pos, pipe, len, flags);
                if (unlikely(ret <= 0))
-                       goto out_release;
+                       goto read_failure;
 
                read_len = ret;
                sd->total_len = read_len;
@@ -909,6 +920,15 @@ done:
        file_accessed(in);
        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:
        /*
         * If we did an incomplete transfer we must release
@@ -937,6 +957,14 @@ static int direct_splice_actor(struct pipe_inode_info *pipe,
                              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
  * @in:                file to splice from
@@ -962,6 +990,7 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
                .flags          = flags,
                .pos            = *ppos,
                .u.file         = out,
+               .splice_eof     = direct_file_splice_eof,
                .opos           = opos,
        };
        long ret;
index a8d708fe08b30d9d17dbf07cb349fe6172e91ae4..72a956d243c2e832ec554e685eac710346872f19 100644 (file)
@@ -2104,6 +2104,7 @@ struct file_operations {
        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_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 **);
        long (*fallocate)(struct file *file, int mode, loff_t offset,
                          loff_t len);
index ba736b457a0683c6358495b738502b9f6b1335fb..9054f17e4b631ef3614a254e05e95ea2ee04bf2a 100644 (file)
@@ -187,6 +187,7 @@ struct proto_ops {
                                      int offset, size_t size, int flags);
        ssize_t         (*splice_read)(struct socket *sock,  loff_t *ppos,
                                       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             (*peek_len)(struct socket *sock);
 
index a55179fd60fc392093907d3b1ab8f1aeaa024f61..41a70687be8533c5681eeb64536609ae8f962770 100644 (file)
@@ -38,6 +38,7 @@ struct splice_desc {
                struct file *file;      /* file to read/write */
                void *data;             /* cookie */
        } u;
+       void (*splice_eof)(struct splice_desc *sd); /* Unexpected EOF handler */
        loff_t pos;                     /* file position */
        loff_t *opos;                   /* sendfile: output position */
        size_t num_spliced;             /* number of bytes already spliced */
index 3158cf0269ac91b8382414946e5f076373223b3d..b987074f8096511e81bce99421080e0a76c7d71c 100644 (file)
@@ -1229,6 +1229,7 @@ struct proto {
                                           int *addr_len);
        int                     (*sendpage)(struct sock *sk, struct page *page,
                                        int offset, size_t size, int flags);
+       void                    (*splice_eof)(struct socket *sock);
        int                     (*bind)(struct sock *sk,
                                        struct sockaddr *addr, int addr_len);
        int                     (*bind_add)(struct sock *sk,
index bb2a209e3e28dc095b8201e054fc5dd1e8c32338..1d71fa44ace45c74a4774ae81e3015fbde290d66 100644 (file)
@@ -129,6 +129,7 @@ static ssize_t sock_sendpage(struct file *file, struct page *page,
 static ssize_t sock_splice_read(struct file *file, loff_t *ppos,
                                struct pipe_inode_info *pipe, size_t len,
                                unsigned int flags);
+static void sock_splice_eof(struct file *file);
 
 #ifdef CONFIG_PROC_FS
 static void sock_show_fdinfo(struct seq_file *m, struct file *f)
@@ -163,6 +164,7 @@ static const struct file_operations socket_file_ops = {
        .sendpage =     sock_sendpage,
        .splice_write = generic_splice_sendpage,
        .splice_read =  sock_splice_read,
+       .splice_eof =   sock_splice_eof,
        .show_fdinfo =  sock_show_fdinfo,
 };
 
@@ -1037,6 +1039,14 @@ static ssize_t sock_splice_read(struct file *file, loff_t *ppos,
        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)
 {
        struct file *file = iocb->ki_filp;