LV06-12-ConfigFS-02-数据结构与应用

我们来详细了解一下Configfs。若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
PC端开发环境 Windows Windows11
Ubuntu Ubuntu20.04.2的64位版本
VMware® Workstation 17 Pro 17.6.0 build-24238078
终端软件 MobaXterm(Professional Edition v23.0 Build 5042 (license))
Win32DiskImager Win32DiskImager v1.0
Linux开发板环境 Linux开发板 正点原子 i.MX6ULL Linux 阿尔法开发板
uboot NXP官方提供的uboot,使用的uboot版本为U-Boot 2019.04
linux内核 linux-4.19.71(NXP官方提供)
点击查看本文参考资料
分类 网址 说明
官方网站 https://www.arm.com/ ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档
https://www.nxp.com.cn/ NXP官方网站
https://www.nxpic.org.cn/NXP 官方社区
https://u-boot.readthedocs.io/en/latest/u-boot官网
https://www.kernel.org/linux内核官网
点击查看相关文件下载
分类 网址 说明
NXP https://github.com/nxp-imx NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库
nxp-imx/linux-imx/releases/tag/v4.19.71 NXP linux内核仓库tags中的v4.19.71
nxp-imx/uboot-imx/releases/tag/rel_imx_4.19.35_1.1.0 NXP u-boot仓库tags中的rel_imx_4.19.35_1.1.0
I.MX6ULL i.MX 6ULL Applications Processors for Industrial Products I.MX6ULL 芯片手册(datasheet,可以在线查看)
i.MX 6ULL Applications ProcessorReference Manual I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网)
Source Code https://elixir.bootlin.com/linux/latest/source linux kernel源码
kernel/git/stable/linux.git - Linux kernel stable tree linux kernel源码(官网,tag 4.19.71)
https://elixir.bootlin.com/u-boot/latest/source uboot源码

这一部分是学习如何创建内核对象, 然后生成对应的文件和目录。

一、核心数据结构

1. 关键数据结构

站在用户的角度来说,一个文件系统里面有目录、文件两种对象。在configfs的内核实现中,对应4个概念。

1.1 configfs_attribute

这里其实还有个configfs_bin_attribute,他们两个都是对应文件。

1
2
3
4
5
6
7
struct configfs_attribute {
const char *ca_name;
struct module *ca_owner;
umode_t ca_mode;
ssize_t (*show)(struct config_item *, char *);
ssize_t (*store)(struct config_item *, const char *, size_t);
};
1
2
3
4
5
6
7
struct configfs_bin_attribute {
struct configfs_attribute cb_attr; /* std. attribute */
void *cb_private; /* for user */
size_t cb_max_size; /* max core size */
ssize_t (*read)(struct config_item *, void *, size_t);
ssize_t (*write)(struct config_item *, const void *, size_t);
};

(1)读写文件时,会导致上述结构体里的show/store或者read/write函数被调用

(2)文件是位于某个目录的: config_item

1.2 config_item

1
2
3
4
5
6
7
8
9
10
11
struct config_item {
char *ci_name;
char ci_namebuf[CONFIGFS_ITEM_NAME_LEN];
struct kref ci_kref;
struct list_head ci_entry;
struct config_item *ci_parent;
struct config_group *ci_group;
const struct config_item_type *ci_type;
struct dentry *ci_dentry;
};

这是 ConfigFS 中最基本的数据结构, 用于表示一个配置项。 每个配置项都是一个内核对象, 可以是设备、 驱动程序、 子系统等。 config_item 结构包含了配置项的类型、名称、 属性、 状态等信息, 以及指向父配置项和子配置项的指针。

Tips:configfs中的每个对象都是config_item,后面的config_group、subsystem本质上都属于特殊的config_item

image-20230328101747270

1.3 config_group

config_group 是一种特殊类型的配置项, 表示一个配置项组,对应一个目录。 它可以包含一组相关的配置项, 形成一个层次结构。config_group 结构包含了父配置项的指针, 以及指向子配置项的链表。

1
2
3
4
5
6
7
struct config_group {
struct config_item cg_item;
struct list_head cg_children;
struct configfs_subsystem *cg_subsys;
struct list_head default_groups;
struct list_head group_entry;
};

1.4 configfs_subsystem

configfs_subsystem 是configfs文件系中的最顶层的数据结构, 用于表示整个 ConfigFS 子系统。 它包含了根配置项组的指针, 以及 ConfigFS 的其他属性和状态信息。

1
2
3
4
struct configfs_subsystem {
struct config_group su_group;
struct mutex su_mutex;
};
  • 比如:/sys/kernel/config/usb_gadget/sys/kernel/config/iio

  • drivers/usb/gadget/configfs.c中调用configfs_register_subsystem(&gadget_subsys)就会创建subsystem,它对应configfs文件系统中的顶层目录usb_gadget

  • subsystem也属于config_group

image-20230328103329215

1.5 总结

这些数据结构之间的关系可以形成一个树形结构, 其中 configfs_subsystem 是根节点, config_group 表示配置项组, config_item 表示单个配置项。 子配置项通过链表连接在一起, 形成父子关系。

image-20250307100009404

2. 子系统、 容器和 config_item 

接下来以设备树插件的驱动为例来详细了解一下这三个概念。

2.1 dtbocfg_root_subsys

1
2
3
4
5
6
7
8
9
10
static struct configfs_subsystem dtbocfg_root_subsys = {
.su_group = {
.cg_item = {
.ci_namebuf = "device-tree",
.ci_type = &dtbocfg_root_type,
},
},
.su_mutex = __MUTEX_INITIALIZER(dtbocfg_root_subsys.su_mutex),
};

这段代码定义了一个名为 dtbocfg_root_subsys 的 configfs_subsystem 结构体实例, 表示ConfigFS 中的一个子系统。

.ci_namebuf = “device-tree”: 配置项的名称设置为”device-tree”, 表示该配置项的名称为”device-tree”。

.ci_type = &dtbocfg_root_type: 配置项的类型设置为 dtbocfg_root_type, 这是一个自定义的配置项类型。

  • .su_mutex 字段是一个互斥锁, 用于保护子系统的操作。 在这里, 使用了__MUTEX_INITIALIZER 宏来初始化互斥锁。

通过这段代码, 创建了一个名为”device-tree”的子系统, 它的根配置项组为空。 可以在该子系统下添加更多的配置项和配置项组, 用于动态配置和管理设备树相关的内核对象。 Linux系统下创建了 device-tree 这个子系统,如下图 :

image-20250307141505657

2.2 注册配置项组

接下来继续分析设备树插件驱动代码中注册配置项组的部分 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
static int __init dtbocfg_module_init(void)
{
int retval = 0;

pr_info(DRIVER_NAME ": " DRIVER_VERSION "\n");

config_group_init(&dtbocfg_root_subsys.su_group);
config_group_init_type_name(&dtbocfg_overlay_group, "overlays", &dtbocfg_overlays_type);

retval = configfs_register_subsystem(&dtbocfg_root_subsys);
if (retval != 0) {
pr_err( "%s: couldn't register subsys\n", __func__);
goto register_subsystem_failed;
}

retval = configfs_register_group(&dtbocfg_root_subsys.su_group, &dtbocfg_overlay_group);
if (retval != 0) {
pr_err( "%s: couldn't register group\n", __func__);
goto register_group_failed;
}

pr_info(DRIVER_NAME ": OK\n");
return 0;

register_group_failed:
configfs_unregister_subsystem(&dtbocfg_root_subsys);
register_subsystem_failed:
return retval;
}

这段代码是一个初始化函数 dtbocfg_module_init(), 用于初始化和注册 ConfigFS 子系统和配置项组。

  • 第 7 行:通过 config_group_init()函数初始化了 dtbocfg_root_subsys.su_group, 即子系统的根配置项组。

  • 第 8 行:使用 config_group_init_type_name()函数初始化了dtbocfg_overlay_group, 表示名为”overlays”的配置项组, 并指定了配置项组的类型为dtbocfg_overlays_type, 这是一个自定义的配置项类型。

  • 第 10 行:调用 configfs_register_subsystem()函数注册了 dtbocfg_root_subsys 子系统。 如果注册失败, 将打印错误信息, 并跳转到register_subsystem_failed 标签处进行错误处理。

  • 第 16 行:调用 configfs_register_group()函数注册了 dtbocfg_overlay_group 配置项组, 并将其添加到 dtbocfg_root_subsys.su_group 下。 如果注册失败, 同样会打印错误信息, 并跳转到 register_group_failed 标签处进行错误处理。

  • 第 22 - 23 行:如果所有的注册过程都成功, 将打印”OK”消息, 并返回 0, 表示初始化成功。

如果在注册配置项组失败时, 会先调用 configfs_unregister_subsystem() 函数注销之前注册的子系统, 然后返回注册失败的错误码 retval。这段代码的作用是初始化和注册一个名为”device-tree”的 ConfigFS 子系统, 并在其下创建一个名为”overlays”的配置项组。 Linux 系统下, 在 device-tree 子系统下创建了 overlays 容器,如下图 :

image-20250307142437264

3. 属性和方法

我们要在容器下放目录或属性文件, 所以我们看一下 config_item结构体 :

1
2
3
4
5
6
7
8
9
10
11
struct config_item {
char *ci_name;
char ci_namebuf[CONFIGFS_ITEM_NAME_LEN]; // 目录的名字
struct kref ci_kref;
struct list_head ci_entry;
struct config_item *ci_parent;
struct config_group *ci_group;
const struct config_item_type *ci_type; // 目录下属性文件和属性操作
struct dentry *ci_dentry;
};

config_item 结构体中包含了 config_item_type 结构体, config_item_type 结构体如下所示:

1
2
3
4
5
6
7
struct config_item_type {
struct module *ct_owner;
struct configfs_item_operations *ct_item_ops; // item(目录) 的操作方法
struct configfs_group_operations *ct_group_ops; // group(容器) 的操作方法
struct configfs_attribute **ct_attrs; // 属性文件的操作方法
struct configfs_bin_attribute **ct_bin_attrs;// bin 属性文件的操作方法
};
1
2
3
4
5
6
struct configfs_item_operations {
// 删除 item 方法, 在 group 下面使用 rmdir 命令会调用这个方法
void (*release)(struct config_item *);
int (*allow_link)(struct config_item *src, struct config_item *target);
void (*drop_link)(struct config_item *src, struct config_item *target);
};
1
2
3
4
5
6
7
8
9
struct configfs_group_operations {
//创建 item 的方法, 在 group 下面使用 mkdir 命令会调用这个方法
struct config_item *(*make_item)(struct config_group *group, const char *name);
//创建 group 的方法
struct config_group *(*make_group)(struct config_group *group, const char *name);
int (*commit_item)(struct config_item *item);
void (*disconnect_notify)(struct config_group *group, struct config_item *item);
void (*drop_item)(struct config_group *group, struct config_item *item);
};
1
2
3
4
5
6
7
8
9
struct configfs_attribute {
const char *ca_name; // 属性文件的名字
struct module *ca_owner;// 属性文件文件的所属模块
umode_t ca_mode; // 属性文件访问权限

// 读写方法的函数指针, 具体功能需要自行实现。
ssize_t (*show)(struct config_item *, char *);
ssize_t (*store)(struct config_item *, const char *, size_t);
};

4. 总结

image-20250313090707277 image-20250313090813358 image-20250313090903830
  • attribute
image-20250313091102610

当我们使用mkdir dtbo_demo这个命令创建dtbo_demo这个item的时候,就会调用configfs_group_operations .make_item方法

二、注册configfs子系统

1. demo源码

demo源码可以看这里:12_configfs_demo/01_configfs_subsystem · 苏木/imx6ull-driver-demo - 码云 - 开源中国

2. 开发板测试

  • (1)挂载configfs文件系统
1
mount -t configfs none /sys/kernel/config
  • (2)加载驱动
1
insmod sdriver_demo.ko
image-20250313092623759
  • (3)查看注册的子系统
1
ls /sys/kernel/config/
image-20250313092724621
  • (4)卸载驱动
1
2
rmmod sdriver_demo.ko
ls /sys/kernel/config/
image-20250313092830742

三、注册group容器

1. demo源码

源码可以看这里:12_configfs_demo/02_configfs_group · 苏木/imx6ull-driver-demo - 码云 - 开源中国

2. 开发板测试

  • (1)挂载configfs文件系统
1
mount -t configfs none /sys/kernel/config
  • (2)测试效果
1
2
3
4
5
6
7
8
insmod sdriver_demo.ko

ls /sys/kernel/config/
ls /sys/kernel/config/sconfigfs/
ls /sys/kernel/config/sconfigfs/sgroup/

rmmod sdriver_demo.ko
ls /sys/kernel/config/
image-20250313094035087

我们进入注册生成的 sconfigfs 子系统, 可以看到注册生成的 sgroup 容器。

四、用户空间创建 item

1. demo实例

1.1 demo源码

源码可以看这里:12_configfs_demo/03_configfs_item · 苏木/imx6ull-driver-demo - 码云 - 开源中国

1.2 开发板测试

  • (1)挂载configfs文件系统
1
mount -t configfs none /sys/kernel/config
  • (2)加载驱动
1
insmod sdriver_demo.ko
image-20250313105648767
  • (3)创建item
1
2
cd /sys/kernel/config/sconfigfs/sgroup/
mkdir item_demo
image-20250313105722490
  • (4)删除item
1
2
cd /sys/kernel/config/sconfigfs/sgroup/
rm -rf item_demo
image-20250313105818715
  • (5)卸载驱动
1
rmmod sdriver_demo.ko

2. drop_item 和 release

当我们在命令行使用 rmdir 命令删除 item 时, 会执行configfs_item_operations.release 函数,

1
2
3
4
5
struct configfs_item_operations {
void (*release)(struct config_item *);
int (*allow_link)(struct config_item *src, struct config_item *target);
void (*drop_link)(struct config_item *src, struct config_item *target);
};

.release 成员字段是在 struct config_item_type结构体中成员configfs_item_operations里面定义的一个回调函数指针。 它指向一个函数, 当 configfs 中的配置项被释放或删除时, 内核会调用该函数来执行相应的资源释放操作。 它通常用于释放与配置项相关的资源, 比如释放动态分配的内存、 关闭打开的文件描述符等。

还有一个 drop_item 函数,它是定义在 configfs_group_operations 中:

1
2
3
4
5
6
7
struct configfs_group_operations {
struct config_item *(*make_item)(struct config_group *group, const char *name);
struct config_group *(*make_group)(struct config_group *group, const char *name);
int (*commit_item)(struct config_item *item);
void (*disconnect_notify)(struct config_group *group, struct config_item *item);
void (*drop_item)(struct config_group *group, struct config_item *item);
};

drop_item 一个回调函数指针。 它指向一个函数, 当 configfs 中的配置组(group) 被删除时, 内核会调用该函数来处理与配置组相关的操作。 这个函数通常用于清理配置组的状态、 释放相关的资源以及执行其他必要的清理操作。 .drop_item 函数在删除配置组时被调用, 而不是在删除单个配置项时被调用。

.release 成员字段用于配置项的释放操作, 而 .drop_item 成员字段用于配置组的删除操作。它们分别在不同的上下文中执行不同的任务, 但都与资源释放和清理有关。

3. drop_item实例

2.1 demo源码

源码可以看这里:12_configfs_demo/04_drop_and_release · 苏木/imx6ull-driver-demo - 码云 - 开源中国

3.2 开发板测试

  • (1)挂载configfs文件系统
1
mount -t configfs none /sys/kernel/config
  • (2)加载驱动
1
insmod sdriver_demo.ko
  • (3)创建item
1
2
cd /sys/kernel/config/sconfigfs/sgroup/
mkdir item_demo
  • (4)删除item
1
2
cd /sys/kernel/config/sconfigfs/sgroup/
rm -rf item_demo
image-20250313112643471

输入“ rmdir test” 命令删除 item, 执行 rmdir 命令之后, 依次执行了 sdrv_group_delete_item() 函数和sdrv_item_release()函数。

  • (5)卸载驱动
1
rmmod sdriver_demo.ko

五、注册attribute

1. demo源码

源码可以看这里:12_configfs_demo/05_attribute · 苏木/imx6ull-driver-demo - 码云 - 开源中国

2. 开发板测试

  • (1)挂载configfs文件系统
1
mount -t configfs none /sys/kernel/config
  • (2)加载驱动
1
insmod sdriver_demo.ko
  • (3)创建item
1
2
cd /sys/kernel/config/sconfigfs/sgroup/
mkdir item_demo
image-20250313130003520
  • (4)属性操作
1
2
3
cd item_demo
cat read
echo 1 > write
image-20250313130056103
  • (5)删除item
1
2
3
4
cd /sys/kernel/config/sconfigfs/sgroup/
rmdir item_demo
# 下面这个命令在有属性文件的时候会无权限
rm -rf item_demo
image-20250313130324320
  • (5)卸载驱动
1
rmmod sdriver_demo.ko

六、实现多级目录

经过前面的学习, 我们了解到一个配置组也就是(group) 可以包含多个配置项(item)和子配置组。 配置项和配置组都是作为配置组的成员存在的。 配置项和配置组之间通过指针进行关联, 以形成一个层次结构。

配置项(item)是最后一级目录了,它下面不可以再有目录:

image-20250316141505803

我们可以在配置项组中创建目录,那么我们能否实现这样的结构:

1
/sys/kernel/config/sconfigfs/sgroup/a_dir/item_name_dir

下面就来了解一下多级目录的创建。其实吧,学到这里的时候我也不知道这里有什么用,后面用到了再补充吧。

1. demo源码

源码可以看这里:12_configfs_demo/06_multilevel_dir · 苏木/imx6ull-driver-demo - 码云 - 开源中国

2. 开发板测试

  • (1)挂载configfs文件系统
1
mount -t configfs none /sys/kernel/config
  • (2)加载驱动
1
insmod sdriver_demo.ko
  • (3)创建目录
1
2
cd /sys/kernel/config/sconfigfs/sgroup/
mkdir group_demo

然后输入“mkdir group_demo” 命令创建 config_item ,创建成功之后 会打印这个项的名称:

image-20250313143111469

然后进入group_demo目录下, 此时可以在 test 目录下创建 item —— item_demo:

image-20250313143943356

但由于在驱动中我们并没有实现在 group 下创建 item 功能, 所以会提示创建 item_demo 没有权限。后面没有深入去搞了,这里就了解到这里吧。

  • (5)删除刚才的group_demo
1
2
3
4
cd /sys/kernel/config/sconfigfs/sgroup/
rmdir group_demo
# 或者
rm -rf group_demo
image-20250313144138207
  • (5)卸载驱动
1
rmmod sdriver_demo.ko

参考资料

configfs-用户空间控制的内核对象配置 - abin在路上 - 博客园

14_configfs的使用与内部机制 — Linux设备驱动开发教程中心

Welcome to 100ask’s documentation — Linux设备驱动开发教程中心