Okay. Let's go with that. So I have to put the handling in vfs_splice_read(): long vfs_splice_read(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned int flags) { ... if (unlikely(!in->f_op->splice_read)) return warn_unsupported(in, "read"); /* * O_DIRECT and DAX don't deal with the pagecache, so we * allocate a buffer, copy into it and splice that into the pipe. */ if ((in->f_flags & O_DIRECT) || IS_DAX(in->f_mapping->host)) return copy_splice_read(in, ppos, pipe, len, flags); return in->f_op->splice_read(in, ppos, pipe, len, flags); } which leaves very little in generic_file_splice_read: ssize_t generic_file_splice_read(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned int flags) { if (unlikely(*ppos >= in->f_mapping->host->i_sb->s_maxbytes)) return 0; if (unlikely(!len)) return 0; return filemap_splice_read(in, ppos, pipe, len, flags); } so I wonder if the tests in generic_file_splice_read() can be folded into vfs_splice_read(), pointers to generic_file_splice_read() be replaced with pointers to filemap_splice_read() and generic_file_splice_read() just be removed. I suspect we can't quite do this because of the *ppos check - but I wonder if that's actually necessary since filemap_splice_read() checks against i_size... or if the check can be moved there if we definitely want to do it. Certainly, the zero-length check can be done in vfs_splice_read(). David