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>
        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.
  */
 
                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;
        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
                              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
                .flags          = flags,
                .pos            = *ppos,
                .u.file         = out,
+               .splice_eof     = direct_file_splice_eof,
                .opos           = opos,
        };
        long ret;
 
        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);
 
                                      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);
 
 
                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 */
 
                                           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,
 
 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)
        .fasync =       sock_fasync,
        .splice_write = splice_to_socket,
        .splice_read =  sock_splice_read,
+       .splice_eof =   sock_splice_eof,
        .show_fdinfo =  sock_show_fdinfo,
 };
 
        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;