]> 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, 10 Jan 2024 16:10:27 +0000 (17:10 +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: a0002127cd74 ("udp: move udp->no_check6_tx to udp->udp_flags")
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 5969b7a1d353a8923452415140bfd743b2f188c8..c4ae54deac42c000691816c2880597837cb48c42 100644 (file)
@@ -764,6 +764,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.
  */
@@ -864,7 +875,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;
@@ -904,6 +915,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
@@ -932,6 +952,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
@@ -957,6 +985,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 b6af6abc7a77f7f667254afb09161e6388acc66c..4a1911dcf834b8fc548d1c733101caf38e7cab1a 100644 (file)
@@ -2177,6 +2177,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 18d942bbdf6e0977f4efe20e0ce17e4092910bd0..25baca60f6cba018ef51ca345351dfd5d3fc365e 100644 (file)
@@ -209,6 +209,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 d8ed62a8e1a3ec6c08929e1c2addef2c4e8a410b..9de9f070537cc05fe7147bb00d5b2280ecfec443 100644 (file)
@@ -1279,6 +1279,7 @@ struct proto {
                                           size_t len, int flags, 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 6f39f7b0cc85c838a171a2edb3759a19778199df..639d76f20384e62845cdb0387982b996a40c57d0 100644 (file)
@@ -130,6 +130,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)
@@ -164,6 +165,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,
 };
 
@@ -1091,6 +1093,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;