[RFC PATCHv3 5/6] nvme: add generic debugfs support
Hannes Reinecke
hare at suse.de
Mon Oct 27 04:55:54 PDT 2025
On 10/27/25 10:29, Nilay Shroff wrote:
> Add generic infrastructure for creating and managing debugfs files in
> the NVMe module. This introduces helper APIs that allow NVMe drivers to
> register and unregister debugfs entries, along with a reusable attribute
> structure for defining new debugfs files.
>
> The implementation uses seq_file interfaces to safely expose per-NS and
> per-NS-head statistics, while supporting both simple show callbacks and
> full seq_operations.
>
> Reviewed-by: Hannes Reinecke <hare at suse.de>
> Signed-off-by: Nilay Shroff <nilay at linux.ibm.com>
> ---
> drivers/nvme/host/Makefile | 2 +-
> drivers/nvme/host/debugfs.c | 122 ++++++++++++++++++++++++++++++++++++
> drivers/nvme/host/nvme.h | 11 ++++
> 3 files changed, 134 insertions(+), 1 deletion(-)
> create mode 100644 drivers/nvme/host/debugfs.c
>
> diff --git a/drivers/nvme/host/Makefile b/drivers/nvme/host/Makefile
> index 6414ec968f99..7962dfc3b2ad 100644
> --- a/drivers/nvme/host/Makefile
> +++ b/drivers/nvme/host/Makefile
> @@ -10,7 +10,7 @@ obj-$(CONFIG_NVME_FC) += nvme-fc.o
> obj-$(CONFIG_NVME_TCP) += nvme-tcp.o
> obj-$(CONFIG_NVME_APPLE) += nvme-apple.o
>
> -nvme-core-y += core.o ioctl.o sysfs.o pr.o
> +nvme-core-y += core.o ioctl.o sysfs.o pr.o debugfs.o
> nvme-core-$(CONFIG_NVME_VERBOSE_ERRORS) += constants.o
> nvme-core-$(CONFIG_TRACING) += trace.o
> nvme-core-$(CONFIG_NVME_MULTIPATH) += multipath.o
> diff --git a/drivers/nvme/host/debugfs.c b/drivers/nvme/host/debugfs.c
> new file mode 100644
> index 000000000000..5c441779554f
> --- /dev/null
> +++ b/drivers/nvme/host/debugfs.c
> @@ -0,0 +1,122 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 IBM Corporation
> + * Nilay Shroff <nilay at linux.ibm.com>
> + */
> +
> +#include <linux/debugfs.h>
> +#include <linux/seq_file.h>
> +
> +#include "nvme.h"
> +
> +struct nvme_debugfs_attr {
> + const char *name;
> + umode_t mode;
> + int (*show)(void *data, struct seq_file *m);
> + const struct seq_operations *seq_ops;
> +};
> +
> +struct nvme_debugfs_ctx {
> + void *data;
> + struct nvme_debugfs_attr *attr;
> + int srcu_idx;
> +};
> +
> +static int nvme_debugfs_show(struct seq_file *m, void *v)
> +{
> + struct nvme_debugfs_ctx *ctx = m->private;
> + void *data = ctx->data;
> + struct nvme_debugfs_attr *attr = ctx->attr;
> +
> + return attr->show(data, m);
> +}
> +
> +static int nvme_debugfs_open(struct inode *inode, struct file *file)
> +{
> + void *data = inode->i_private;
> + struct nvme_debugfs_attr *attr = debugfs_get_aux(file);
> + struct nvme_debugfs_ctx *ctx;
> + struct seq_file *m;
> + int ret;
> +
> + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> + if (WARN_ON_ONCE(!ctx))
> + return -ENOMEM;
> +
> + ctx->data = data;
> + ctx->attr = attr;
> +
> + if (attr->seq_ops) {
> + ret = seq_open(file, attr->seq_ops);
> + if (ret) {
> + kfree(ctx);
> + return ret;
> + }
> + m = file->private_data;
> + m->private = ctx;
> + return ret;
> + }
> +
> + if (WARN_ON_ONCE(!attr->show)) {
> + kfree(ctx);
> + return -EPERM;
> + }
> +
> + return single_open(file, nvme_debugfs_show, ctx);
> +}
> +
> +static int nvme_debugfs_release(struct inode *inode, struct file *file)
> +{
> + struct seq_file *m = file->private_data;
> + struct nvme_debugfs_ctx *ctx = m->private;
> + struct nvme_debugfs_attr *attr = ctx->attr;
> + int ret;
> +
> + if (attr->seq_ops)
> + ret = seq_release(inode, file);
> + else
> + ret = single_release(inode, file);
> +
> + kfree(ctx);
> + return ret;
> +}
> +
> +static const struct file_operations nvme_debugfs_fops = {
> + .owner = THIS_MODULE,
> + .open = nvme_debugfs_open,
> + .read = seq_read,
> + .llseek = seq_lseek,
> + .release = nvme_debugfs_release,
> +};
> +
> +
> +static const struct nvme_debugfs_attr nvme_mpath_debugfs_attrs[] = {
> + {},
> +};
> +
> +static const struct nvme_debugfs_attr nvme_ns_debugfs_attrs[] = {
> + {},
> +};
> +
> +static void nvme_debugfs_create_files(struct request_queue *q,
> + const struct nvme_debugfs_attr *attr, void *data)
> +{
> + if (WARN_ON_ONCE(!q->debugfs_dir))
> + return;
> +
> + for (; attr->name; attr++)
> + debugfs_create_file_aux(attr->name, attr->mode, q->debugfs_dir,
> + data, (void *)attr, &nvme_debugfs_fops);
> +}
> +
> +void nvme_debugfs_register(struct gendisk *disk)
> +{
> + const struct nvme_debugfs_attr *attr;
> +
> + if (nvme_disk_is_ns_head(disk))
> + attr = nvme_mpath_debugfs_attrs;
> + else
> + attr = nvme_ns_debugfs_attrs;
> +
> + nvme_debugfs_create_files(disk->queue, attr, disk->private_data);
> +}
> diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
> index 2e58d4d6902a..d049b62b71d2 100644
> --- a/drivers/nvme/host/nvme.h
> +++ b/drivers/nvme/host/nvme.h
> @@ -988,6 +988,7 @@ extern const struct attribute_group *nvme_dev_attr_groups[];
> extern const struct block_device_operations nvme_bdev_ops;
>
> void nvme_delete_ctrl_sync(struct nvme_ctrl *ctrl);
> +
> struct nvme_ns *nvme_find_path(struct nvme_ns_head *head, unsigned int op_type);
> static inline int nvme_data_dir(const enum req_op op)
> {
> @@ -999,6 +1000,16 @@ static inline int nvme_data_dir(const enum req_op op)
> return NVME_STAT_OTHER;
> }
>
> +void nvme_debugfs_register(struct gendisk *disk);
> +static inline void nvme_debugfs_unregister(struct gendisk *disk)
> +{
> + /*
> + * Nothing to do for now. When the request queue is unregistered,
> + * all files under q->debugfs_dir are recursively deleted.
> + * This is just a placeholder; the compiler will optimize it out.
> + */
> +}
> +
> #ifdef CONFIG_NVME_MULTIPATH
> static inline bool nvme_ctrl_use_ana(struct nvme_ctrl *ctrl)
> {
Especially as you already add a debugfs entry, so moving
the values from the previous two patches should be easy.
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare at suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
More information about the Linux-nvme
mailing list