概述

Debugfs是内核开发人员向用户空间提供信息的一种简单方法。 开发人员可以把他们想要的任何信息放在那里。
Debugfs通常使用如下命令进行挂载:

  1. mount -t debugfs none /sys/kernel/debug

或者在/etc/fstab中增加一行
默认情况下,只有root用户可以访问debufgfs根目录. To change access to the tree the “uid”, “gid” and “mode” mount options can be used.
使用debugfs时需要包含头文件:

  1. #include <linux/debugfs.h>.

还需要在deconfig文件中,设置CONFIG_DEBUG_FS

  1. CONFIG_DEBUG_FS=y

然后,要先创建至少一个目录来存放一组debugfs文件:

  1. struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);

关于这个接口的说明如下:

  • 如果调用成功,将在指定的parent目录下创建一个名为name的目录;
  • 如果parent为NULL,则该目录将在debugfs根目录中创建name目录;
  • 成功执行时,返回值是一个struct dentry指针,这个指针可用于在name目录中创建文件,并且在最后清理文件时也要用到这个指针
  • 执行失败时,返回值为ERR_PTR(-ERROR) 。注意这里的ERROR是errno。如果返回值为ERR_PTR(-ENODEV),这表明编译时没有把debugfs编译到内核

The most general way to create a file within a debugfs directory is with:
在debugfs目录中创建文件的最常用方法是使用下面的接口:

  1. struct dentry *debugfs_create_file(const char *name, umode_t mode,
  2. struct dentry *parent, void *data,
  3. const struct file_operations *fops);

关于这个接口的说明:

  • name是要创建的文件的名称
  • mode描述name文件的访问权限
  • parent表示存放name文件的目录
  • data将被存储在生成的inode结构的i_private字段中
  • fops是一组文件操作接口。至少,应该提供read()和/或write()操作;其他可以根据需求决定是否包括在内。
  • 返回值将是一个指向创建文件的dentry指针,错误时为ERR_PTR(-ERROR),如果不支持debugfs,则返回为ERR_PTR(-ENODEV)。

创建一个初始大小的文件,可以使用下面的函数:

  1. void debugfs_create_file_size(const char *name, umode_t mode,
  2. struct dentry *parent, void *data,
  3. const struct file_operations *fops,
  4. loff_t file_size);

关于这个接口的说明:

  • File_size为文件的初始大小。其他参数与函数debugfs_create_file相同。

在许多情况下,实际上并不需要创建一组文件操作;debugfs代码为简单的情况提供了许多辅助函数。
创建一个文件,文件内写入了一个十进制整数:

  1. void debugfs_create_u8(const char *name, umode_t mode,
  2. struct dentry *parent, u8 *value);
  3. void debugfs_create_u16(const char *name, umode_t mode,
  4. struct dentry *parent, u16 *value);
  5. void debugfs_create_u32(const char *name, umode_t mode,
  6. struct dentry *parent, u32 *value);
  7. void debugfs_create_u64(const char *name, umode_t mode,
  8. struct dentry *parent, u64 *value);

关于这个接口的说明:

  • value是创建文件时要写入的整数

创建一个文件,文件内写入了一个十六进制整数::

  1. void debugfs_create_x8(const char *name, umode_t mode,
  2. struct dentry *parent, u8 *value);
  3. void debugfs_create_x16(const char *name, umode_t mode,
  4. struct dentry *parent, u16 *value);
  5. void debugfs_create_x32(const char *name, umode_t mode,
  6. struct dentry *parent, u32 *value);
  7. void debugfs_create_x64(const char *name, umode_t mode,
  8. struct dentry *parent, u64 *value);

只要开发人员知道要导出的值的大小,可以用下面的函数导出数值。但是,有些类型在不同的体系架构上有不同的宽度,这在一定程度上使情况复杂化,这些函数如下:
下面函数将创建一个debugfs文件来表示size_t类型的变量:

  1. void debugfs_create_size_t(const char *name, umode_t mode,
  2. struct dentry *parent, size_t *value);

导出unsigned long变量的接口如下:

  1. struct dentry *debugfs_create_ulong(const char *name, umode_t mode,
  2. struct dentry *parent,
  3. unsigned long *value);
  4. void debugfs_create_xul(const char *name, umode_t mode,
  5. struct dentry *parent, unsigned long *value);
  • debugfs_create_ulong用于导出十进制unsigned long变量
  • debugfs_create_xul用于到处十六进制unsigned long变量

布尔值可以被放置在debugfs中:

  1. void debugfs_create_bool(const char *name, umode_t mode,
  2. struct dentry *parent, bool *value);

对文件的读取将产生Y(对于非零值)或N,后跟一个换行符。如果写入,它将接受大写(Y/N)或小写值(y/n),或1或0。任何其他输入都将被静默地忽略。

atomic_t值可以放在debugfs中,对该文件的读取将获得atomic_t值,而对该文件的写入将设置atomic_t值。

  1. void debugfs_create_atomic_t(const char *name, umode_t mode,
  2. struct dentry *parent, atomic_t *value)

导出二进制数据块:

  1. struct debugfs_blob_wrapper {
  2. void *data;
  3. unsigned long size;
  4. };
  5. struct dentry *debugfs_create_blob(const char *name, umode_t mode,
  6. struct dentry *parent,
  7. struct debugfs_blob_wrapper *blob);

对该文件的读取将返回debugfs_blob_wrapper结构所指向的数据。可以用于导出一些格式化的数据。注意:debugfs_create_blob创建的文件都是只读的。

导出寄存器块:

  1. struct debugfs_reg32 { //一个寄存器的name和offset
  2. char *name;
  3. unsigned long offset;
  4. };
  5. struct debugfs_regset32 { //一组寄存器。这里的“set”是“a set of register”的“set”
  6. const struct debugfs_reg32 *regs;
  7. int nregs;
  8. void __iomem *base;
  9. struct device *dev; /* Optional device for Runtime PM */
  10. };
  11. struct dentry *debugfs_create_regset32(const char *name, umode_t mode,
  12. struct dentry *parent,
  13. struct debugfs_regset32 *regset);
  14. void debugfs_print_regs32(struct seq_file *s, const struct debugfs_reg32 *regs,
  15. int nregs, void __iomem *base, char *prefix);

debugfs_create_regset32创建寄存器块文件。debugfs_print_regs32将寄存器块打印到seq_file中。

如果你想在debugfs导出一个u32数组,你可以创建文件:

struct debugfs_u32_array {
    u32 *array;
    u32 n_elements;
};

void debugfs_create_u32_array(const char *name, umode_t mode,
                    struct dentry *parent,
                    struct debugfs_u32_array *array);

array 参数包装了一个struct debugfs_u32_array的指针。注意:array一旦创建,其大小就不能更改。

There is a helper function to create device related seq_file:

void debugfs_create_devm_seqfile(struct device *dev,
                             const char *name,
                             struct dentry *parent,
                             int (*read_fn)(struct seq_file *s,
                                     void *data));

dev argument is the device related to this debugfs file,
read_fn is a function pointer which to be called to print the seq_file content.

修改文件名和文件路径:

struct dentry *debugfs_rename(struct dentry *old_dir, //原文件所在的目录的struct dentry
                              struct dentry *old_dentry, //原文件的struct dentry
                              struct dentry *new_dir, //新文件所在的目录的struct dentry
                              const char *new_name); //新文件名

调用 debugfs_rename() 将一个已经存在的debugfs文件重命名为 new_name,也可以修改文件的目录。new_name不能是已经存在的文件的文件名;

创建软连接:

struct dentry *debugfs_create_symlink(const char *name, //软连接名
                                      struct dentry *parent, //软连接所在的目录
                                      const char *target); //目标文件的路径

代码中需要显式主动删除debugfs文件和目录。
删除一个文件或一个目录:

void debugfs_remove(struct dentry *dentry);

递归删除文件和目录:

void debugfs_remove_recursive(struct dentry *dentry); //dentry对应一个目录

示例代码

#if defined(CONFIG_DEBUG_FS)
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/sched.h>

struct lbk_debugfs_data {
    struct dentry *dir;
    struct dentry *dbg;
    u32 command;
};

struct lbk_debugfs_data *dbg = NULL;
#endif


#if defined(CONFIG_DEBUG_FS)
static ssize_t lbk_dbg_read(struct file *file, char __user *userbuf,
        size_t count, loff_t *ppos)
{
    int ret = 0;
    char buf[24];
    ssize_t len = 0;

    if (dbg && (dbg->command != 0)) {
        switch (dbg->command) {
        case LBK_GET_KERNEL_VERSION:
            len = snprintf(buf, sizeof(buf), "%s\n", LB_KERNEL_VERSION);
            break;
        default:
            ret = -EINVAL;
            break;
        }
        dbg->command = 0;
    }

    return ret ? ret : simple_read_from_buffer(userbuf, count, ppos, buf, len);
}

static ssize_t lbk_dbg_write(struct file *file,
        const char __user *ubuf, size_t count, loff_t *ppos)
{
    char *pb;
    char buf[24];
    int ret = 0;
    unsigned int val;

    memset(buf, 0 , sizeof buf);

    if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
        return -EFAULT;

    mutex_lock(&_lb_mutex);
    if (count > 1) {
        pb = buf + 1;
        switch (buf[0]) {
        case 'h':
            lbwdt_info("Usage: [hkusplrfc] [t timeout] [j jiffies] [m msec]\n"
                    "\tj <jiffies>: set watchdog timeout jiffies\n\tm <msec>: set watchdog timeout msec\n\n");
            break;
        case 'k':
            if (dbg) dbg->command = LBK_GET_KERNEL_VERSION;
            lbwdt_info("kernel version: %s\n", LB_KERNEL_VERSION);
            break;
        case 'u':
            if (dbg) dbg->command = LBK_GET_UUID;
            break;
        case 's':
        ...
        default:
            pr_err("%s: Invalid command!\n", __func__);
            ret = -EINVAL;
            break;
        }
    }
    mutex_unlock(&_lb_mutex);

    return ret ? ret : count;
}

static const struct file_operations lbk_dbg_fops = {
    .owner          = THIS_MODULE,
    .open           = simple_open,
    .write          = lbk_dbg_write,
    .read           = lbk_dbg_read,
};

static int lbk_debugfs_init(void)
{
    int ret = 0;
    dbg = kzalloc(sizeof(struct lbk_debugfs_data), GFP_KERNEL);
    if (!dbg) {
        pr_err("Alloc debugfs data memory failed!\n");
        return -ENOMEM;
    }

    dbg->dir = debugfs_create_dir(KBUILD_MODNAME, NULL); // the dir name is same with this file.
    if (IS_ERR(dbg->dir)) {
        ret = PTR_ERR(dbg->dir);
        pr_err("failed to create debugfs dir! error: %d.\n", ret);
        return ret;
    } else {
        dbg->dbg = debugfs_create_file("dbg", S_IRUGO | S_IWUSR, dbg->dir, NULL, &lbk_dbg_fops);
        if (!dbg->dbg) {
            ret = -ENOMEM;
            goto err0;
        }
    }

    return 0;

err0:
    debugfs_remove_recursive(dbg->dir);
    kfree(dbg);
    dbg = NULL;
    return ret;
}

static void lbk_debugfs_exit(void)
{
    if (dbg) {
        if (dbg->dir)
            debugfs_remove_recursive(dbg->dir);
        kfree(dbg);
        dbg = NULL;
    }
}

#endif


static int __init lbwdt_init(void)
{
#if defined(CONFIG_DEBUG_FS)
    lbk_debugfs_init();
#endif
}

static void __exit lbwdt_exit(void)
{
#if defined(CONFIG_DEBUG_FS)
    lbk_debugfs_exit();
#endif
    return;
}