Re: [PATCH v4 06/39] netfs: Add a procfile to list in-progress requests

Jeff Layton
2023-12-13T15:59:30Z

On Wed, 2023-12-13 at 15:23 +0000, David Howells wrote:
> Add a procfile, /proc/fs/netfs/requests, to list in-progress netfslib I/O
> requests.
>

This should probably be in debugfs. I could see us wanting to improve
this interface over time. That's harder with procfs but with debugfs
we'd have carte blanche to do so.

> Signed-off-by: David Howells <dhowells@redhat.com>
> cc: Jeff Layton <jlayton@kernel.org>
> cc: linux-cachefs@redhat.com
> cc: linux-fsdevel@vger.kernel.org
> cc: linux-mm@kvack.org
> ---
> fs/netfs/internal.h | 22 ++++++++++++++
> fs/netfs/main.c | 69 ++++++++++++++++++++++++++++++++++++++++++-
> fs/netfs/objects.c | 4 ++-
> include/linux/netfs.h | 6 +++-
> 4 files changed, 98 insertions(+), 3 deletions(-)
>
> diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h
> index a15fe67e1db7..937d9a22f178 100644
> --- a/fs/netfs/internal.h
> +++ b/fs/netfs/internal.h
> @@ -33,6 +33,28 @@ int netfs_begin_read(struct netfs_io_request *rreq, bool sync);
> * main.c
> */
> extern unsigned int netfs_debug;
> +extern struct list_head netfs_io_requests;
> +extern spinlock_t netfs_proc_lock;
> +
> +#ifdef CONFIG_PROC_FS
> +static inline void netfs_proc_add_rreq(struct netfs_io_request *rreq)
> +{
> + spin_lock(&netfs_proc_lock);
> + list_add_tail_rcu(&rreq->proc_link, &netfs_io_requests);
> + spin_unlock(&netfs_proc_lock);
> +}
> +static inline void netfs_proc_del_rreq(struct netfs_io_request *rreq)
> +{
> + if (!list_empty(&rreq->proc_link)) {
> + spin_lock(&netfs_proc_lock);
> + list_del_rcu(&rreq->proc_link);
> + spin_unlock(&netfs_proc_lock);
> + }
> +}
> +#else
> +static inline void netfs_proc_add_rreq(struct netfs_io_request *rreq) {}
> +static inline void netfs_proc_del_rreq(struct netfs_io_request *rreq) {}
> +#endif
>
> /*
> * objects.c
> diff --git a/fs/netfs/main.c b/fs/netfs/main.c
> index c9af6e0896d3..97ce1436615b 100644
> --- a/fs/netfs/main.c
> +++ b/fs/netfs/main.c
> @@ -21,13 +21,80 @@ unsigned netfs_debug;
> module_param_named(debug, netfs_debug, uint, S_IWUSR | S_IRUGO);
> MODULE_PARM_DESC(netfs_debug, "Netfs support debugging mask");
>
> +#ifdef CONFIG_PROC_FS
> +LIST_HEAD(netfs_io_requests);
> +DEFINE_SPINLOCK(netfs_proc_lock);
> +
> +static const char *netfs_origins[] = {
> + [NETFS_READAHEAD] = "RA",
> + [NETFS_READPAGE] = "RP",
> + [NETFS_READ_FOR_WRITE] = "RW",
> +};
> +
> +/*
> + * Generate a list of I/O requests in /proc/fs/netfs/requests
> + */
> +static int netfs_requests_seq_show(struct seq_file *m, void *v)
> +{
> + struct netfs_io_request *rreq;
> +
> + if (v == &netfs_io_requests) {
> + seq_puts(m,
> + "REQUEST OR REF FL ERR OPS COVERAGE\n"
> + "======== == === == ==== === =========\n"
> + );
> + return 0;
> + }
> +
> + rreq = list_entry(v, struct netfs_io_request, proc_link);
> + seq_printf(m,
> + "%08x %s %3d %2lx %4d %3d @%04llx %zx/%zx",
> + rreq->debug_id,
> + netfs_origins[rreq->origin],
> + refcount_read(&rreq->ref),
> + rreq->flags,
> + rreq->error,
> + atomic_read(&rreq->nr_outstanding),
> + rreq->start, rreq->submitted, rreq->len);
> + seq_putc(m, '\n');
> + return 0;
> +}
> +
> +static void *netfs_requests_seq_start(struct seq_file *m, loff_t *_pos)
> + __acquires(rcu)
> +{
> + rcu_read_lock();
> + return seq_list_start_head(&netfs_io_requests, *_pos);
> +}
> +
> +static void *netfs_requests_seq_next(struct seq_file *m, void *v, loff_t *_pos)
> +{
> + return seq_list_next(v, &netfs_io_requests, _pos);
> +}
> +
> +static void netfs_requests_seq_stop(struct seq_file *m, void *v)
> + __releases(rcu)
> +{
> + rcu_read_unlock();
> +}
> +
> +static const struct seq_operations netfs_requests_seq_ops = {
> + .start = netfs_requests_seq_start,
> + .next = netfs_requests_seq_next,
> + .stop = netfs_requests_seq_stop,
> + .show = netfs_requests_seq_show,
> +};
> +#endif /* CONFIG_PROC_FS */
> +
> static int __init netfs_init(void)
> {
> int ret = -ENOMEM;
>
> if (!proc_mkdir("fs/netfs", NULL))
> goto error;
> -
> + if (!proc_create_seq("fs/netfs/requests", S_IFREG | 0444, NULL,
> + &netfs_requests_seq_ops))
> + goto error_proc;
> #ifdef CONFIG_FSCACHE_STATS
> if (!proc_create_single("fs/netfs/stats", S_IFREG | 0444, NULL,
> netfs_stats_show))
> diff --git a/fs/netfs/objects.c b/fs/netfs/objects.c
> index e17cdf53f6a7..85f428fc52e6 100644
> --- a/fs/netfs/objects.c
> +++ b/fs/netfs/objects.c
> @@ -45,6 +45,7 @@ struct netfs_io_request *netfs_alloc_request(struct address_space *mapping,
> }
> }
>
> + netfs_proc_add_rreq(rreq);
> netfs_stat(&netfs_n_rh_rreq);
> return rreq;
> }
> @@ -76,12 +77,13 @@ static void netfs_free_request(struct work_struct *work)
> container_of(work, struct netfs_io_request, work);
>
> trace_netfs_rreq(rreq, netfs_rreq_trace_free);
> + netfs_proc_del_rreq(rreq);
> netfs_clear_subrequests(rreq, false);
> if (rreq->netfs_ops->free_request)
> rreq->netfs_ops->free_request(rreq);
> if (rreq->cache_resources.ops)
> rreq->cache_resources.ops->end_operation(&rreq->cache_resources);
> - kfree(rreq);
> + kfree_rcu(rreq, rcu);
> netfs_stat_d(&netfs_n_rh_rreq);
> }
>
> diff --git a/include/linux/netfs.h b/include/linux/netfs.h
> index 32faf6c89702..7244ddebd974 100644
> --- a/include/linux/netfs.h
> +++ b/include/linux/netfs.h
> @@ -175,10 +175,14 @@ enum netfs_io_origin {
> * operations to a variety of data stores and then stitch the result together.
> */
> struct netfs_io_request {
> - struct work_struct work;
> + union {
> + struct work_struct work;
> + struct rcu_head rcu;
> + };
> struct inode *inode; /* The file being accessed */
> struct address_space *mapping; /* The mapping being accessed */
> struct netfs_cache_resources cache_resources;
> + struct list_head proc_link; /* Link in netfs_iorequests */
> struct list_head subrequests; /* Contributory I/O operations */
> void *netfs_priv; /* Private data for the netfs */
> unsigned int debug_id;
>

--
Jeff Layton <jlayton@kernel.org>