Add a proc file to export some statistics for debugging purposes. Signed-off-by: David Howells <dhowells@xxxxxxxxxx> cc: Matthew Wilcox <willy@xxxxxxxxxxxxx> cc: Dave Chinner <david@xxxxxxxxxxxxx> cc: Christoph Hellwig <hch@xxxxxx> cc: Jens Axboe <axboe@xxxxxxxxx> cc: linux-fsdevel@xxxxxxxxxxxxxxx --- fs/splice.c | 28 ++++++++++++++++++++++++++++ include/linux/splice.h | 3 +++ mm/filemap.c | 6 +++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/fs/splice.c b/fs/splice.c index 2b1f109a7d4f..831973ea6b3f 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -36,10 +36,15 @@ #include <linux/net.h> #include <linux/socket.h> #include <linux/sched/signal.h> +#include <linux/proc_fs.h> #include "../mm/internal.h" #include "internal.h" +atomic_t splice_stat_filemap_copied, splice_stat_filemap_moved; +static atomic_t splice_stat_directly_copied; +static atomic_t vmsplice_stat_copied, vmsplice_stat_stole; + /* * Splice doesn't support FMODE_NOWAIT. Since pipes may set this flag to * indicate they support non-blocking reads or writes, we must clear it @@ -276,6 +281,7 @@ ssize_t copy_splice_read(struct file *in, loff_t *ppos, remain -= chunk; } + atomic_add(keep, &splice_stat_directly_copied); kfree(bv); return ret; } @@ -1299,6 +1305,7 @@ static int splice_try_to_steal_page(struct pipe_inode_info *pipe, unmap_mapping_folio(folio); if (remove_mapping(folio->mapping, folio)) { folio_clear_mappedtodisk(folio); + atomic_inc(&vmsplice_stat_stole); flags |= PIPE_BUF_FLAG_LRU; goto add_to_pipe; } @@ -1316,6 +1323,7 @@ static int splice_try_to_steal_page(struct pipe_inode_info *pipe, folio_put(folio); folio = copy; offset = 0; + atomic_inc(&vmsplice_stat_copied); add_to_pipe: page = folio_page(folio, offset / PAGE_SIZE); @@ -1905,3 +1913,23 @@ SYSCALL_DEFINE4(tee, int, fdin, int, fdout, size_t, len, unsigned int, flags) return error; } + +static int splice_stats_show(struct seq_file *m, void *data) +{ + seq_printf(m, "filemap: copied=%u moved=%u\n", + atomic_read(&splice_stat_filemap_copied), + atomic_read(&splice_stat_filemap_moved)); + seq_printf(m, "direct : copied=%u\n", + atomic_read(&splice_stat_directly_copied)); + seq_printf(m, "vmsplice: copied=%u stole=%u\n", + atomic_read(&vmsplice_stat_copied), + atomic_read(&vmsplice_stat_stole)); + return 0; +} + +static int splice_stats_init(void) +{ + proc_create_single("fs/splice", S_IFREG | 0444, NULL, splice_stats_show); + return 0; +} +late_initcall(splice_stats_init); diff --git a/include/linux/splice.h b/include/linux/splice.h index 3c5abbd49ff2..4f04dc338010 100644 --- a/include/linux/splice.h +++ b/include/linux/splice.h @@ -98,4 +98,7 @@ extern int splice_grow_spd(const struct pipe_inode_info *, struct splice_pipe_de extern void splice_shrink_spd(struct splice_pipe_desc *); extern const struct pipe_buf_operations default_pipe_buf_ops; + +extern atomic_t splice_stat_filemap_copied, splice_stat_filemap_moved; + #endif diff --git a/mm/filemap.c b/mm/filemap.c index dd144b0dab69..38d38cc826fa 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2872,7 +2872,8 @@ ssize_t splice_folio_into_pipe(struct pipe_inode_info *pipe, struct address_space *mapping; struct folio *copy = NULL; struct page *page; - unsigned int flags = 0; + unsigned int flags = 0, count = 0; + atomic_t *stat = &splice_stat_filemap_copied; ssize_t ret; size_t spliced = 0, offset = offset_in_folio(folio, fpos); @@ -2902,6 +2903,7 @@ ssize_t splice_folio_into_pipe(struct pipe_inode_info *pipe, /* If we succeed in removing the mapping, set LRU flag and add it. */ if (remove_mapping(mapping, folio)) { folio_unlock(folio); + stat = &splice_stat_filemap_moved; flags = PIPE_BUF_FLAG_LRU; goto add_to_pipe; } @@ -2940,8 +2942,10 @@ ssize_t splice_folio_into_pipe(struct pipe_inode_info *pipe, page++; spliced += part; offset = 0; + count++; } + atomic_add(count, stat); if (copy) folio_put(copy); return spliced;