01、ConfigFS 的核心数据结构
::: 理解 ConfigFS 的核心数据结构对于深入使用和定制 ConfigFS 非常重要, 可以帮助开发者更好地进行设备的配置和管理, 提高系统的灵活性和可扩展性。 :::
一、数据结构
ConfigFS的核心结构可以用三个简单概念理解,就像文件系统的文件和文件夹一样:
- 子系统(configfs_subsystem)
这是最顶层的"根目录",负责管理所有配置。它就像整个系统的总开关,记录子系统是否运行等状态。 - 配置组(config_group)
相当于文件夹,用来分类存放相关配置。每个组可以包含子文件夹或配置文件,形成层级结构。每个组都记得自己的"父文件夹",并通过链表把子项串起来。 - 配置项(config_item)
这是最小的配置单元,就像具体的文件。每个配置项保存实际参数(比如设备设置),并知道属于哪个"父容器"(可能是某个文件夹或根目录)。它们通过指针连接成一棵树。
结构关系:
子系统是树的根,配置组像文件夹分层组织内容,配置项就是具体文件。用户可以通过类似文件路径的层级(如/subsystem/group/item
)来操作配置,就像在文件系统里增删改查文件一样简单。
二、结构体关系
struct configfs_subsystem {
struct config_group su_group;
struct mutex su_mutex;
};
2
3
4
configfs_subsystem 结构体中包含 config_group 结构体, config_group 结构体如下所示:
struct config_group {
struct config_item cg_item;
struct list_head cg_children;
struct configfs_subsystem *cg_subsys;
struct list_head struct list_head default_groups;group_entry;
};
2
3
4
5
6
config_group 结构体中包含 config_item 结构体, config_item 结构体如下所示:
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;
};
2
3
4
5
6
7
8
9
10
三、案例
3.1、configfs_subsystem 实例
这段代码定义了一个名为 dtbocfg_root_subsys 的 configfs_subsystem 结构体实例, 表示ConfigFS 中的一个子系统。
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),
};
2
3
4
5
6
7
8
9
这段代码创建了一个名为"device-tree"的内核配置系统,主要包含以下内容:
- 配置项基础结构:
- 它是一个配置组结构体,名字叫dtbocfg_root_subsys
- 根配置项的名称被设定为"device-tree"
- 使用自定义的dtbocfg_root_type类型来管理这个配置项
- 安全机制:
- 内置了一个名为su_mutex的互斥锁
- 通过标准初始化宏__MUTEX_INITIALIZER来确保操作安全
- 这个锁用来防止多个进程同时修改配置时出错
- 功能说明:
- 目前这个配置组是空的,就像一个空文件夹
- 后续可以在这个系统里添加更多配置项和子配置组
- 主要用于动态管理设备树相关的内核设置
简单来说,这就像在系统里建立了一个名为"设备树配置"的管理模块,先搭建好基本框架,之后可以逐步添加具体配置项来管理硬件设备信息。
3.2、config_group 实例化
这段代码的作用是初始化和注册一个名为"device-tree"的 ConfigFS 子系统, 并在其下创建一个名为"overlays"的配置项组。 Linux 系统下, 在 device-tree 子系统下创建了 overlays 容器。
static struct config_group dtbocfg_overlay_group;
static struct configfs_group_operations dtbocfg_overlays_ops = {
.make_item = dtbocfg_overlay_group_make_item,
.drop_item = dtbocfg_overlay_group_drop_item,
};
static struct config_item_type dtbocfg_overlays_type = {
.ct_group_ops = &dtbocfg_overlays_ops,
.ct_owner = THIS_MODULE,
};
static int __init dtbocfg_module_init(void)
{
int retval = 0;
pr_info("%s\n", __func__);
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("%s: OK\n", __func__);
return 0;
register_group_failed:
configfs_unregister_subsystem(&dtbocfg_root_subsys);
register_subsystem_failed:
return retval;
}
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
30
31
32
33
34
35
36
37
38
39
40
41
初始化过程分几步完成:
首先用config_group_init()函数初始化子系统的根配置组dtbocfg_root_subsys.su_group。
接着用config_group_init_type_name()函数初始化名为"overlays"的配置组dtbocfg_overlay_group,这个组使用自定义类型dtbocfg_overlays_type。
然后调用configfs_register_subsystem()注册主子系统dtbocfg_root_subsys:
- 如果注册失败,会打印错误信息并跳转到"registerSubsystemFailed"进行清理
- 成功的话继续下一步
接着用configfs_register_group()把"overlays"配置组注册到根配置组下:
- 如果这一步失败,会打印错误信息并跳转到"registerGroupFailed"进行清理
- 成功的话继续最后步骤
全部成功时,会显示"OK"提示并返回0表示成功
错误处理流程:
- 如果注册子系统失败,直接清理退出
- 如果注册配置组失败,先注销已注册的子系统再清理退出
- 任何失败情况都会返回对应的错误码retval
整个过程像组装乐高:先搭建基础组件,再逐层组装,每装一步都要检查是否成功,出错就立即拆掉已装部分并报错退出。
四、属性和方法
我们要在容器下放目录或属性文件, 所以我们看一下 config_item 结构体, 如下所示:
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;
};
2
3
4
5
6
7
8
9
10
config_item 结构体中包含了 config_item_type 结构体, config_item_type 结构体如下所示:
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 属性文件的操作方法
};
2
3
4
5
6
7
config_item_type 结构体中包含了 struct configfs_item_operations 结构体, 如下所示:
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);
};
2
3
4
5
config_item_type 结构体中包含了 struct configfs_group_operations 结构体, 如下所示:
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);
};
2
3
4
5
6
7
8
config_item_type 结构体中包含了 struct configfs_attribute 结构体 , 如下所示:
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);
};
2
3
4
5
6
7
8
五、config_item实例化
static struct configfs_attribute *dtbocfg_overlay_attrs[] = {
&dtbocfg_overlay_item_attr_status,
NULL,
};
static struct configfs_bin_attribute *dtbocfg_overlay_bin_attrs[] = {
&dtbocfg_overlay_item_attr_dtbo,
NULL,
};
static struct configfs_item_operations dtbocfg_overlay_item_ops = {
.release = dtbocfg_overlay_release,
};
static struct config_item_type dtbocfg_overlay_item_type = {
.ct_item_ops = &dtbocfg_overlay_item_ops,
.ct_attrs = dtbocfg_overlay_attrs,
.ct_bin_attrs = dtbocfg_overlay_bin_attrs,
.ct_owner = THIS_MODULE,
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static struct configfs_group_operations dtbocfg_overlays_ops = {
.make_item = dtbocfg_overlay_group_make_item,
.drop_item = dtbocfg_overlay_group_drop_item,
};
2
3
4
static struct config_item *dtbocfg_overlay_group_make_item(struct config_group *group, const char *name)
{
struct dtbocfg_overlay_item *overlay;
pr_debug("%s\n", __func__);
overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
if (!overlay)
return ERR_PTR(-ENOMEM);
overlay->id = -1;
overlay->dtbo = NULL;
overlay->dtbo_size = 0;
config_item_init_type_name(&overlay->item, name, &dtbocfg_overlay_item_type);
return &overlay->item;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17