Linux文件系统:2、configfs详解

Linux文件系统:2、configfs详解

码农世界 2024-05-15 前端 66 次浏览 0个评论

目录

🍅点击这里查看所有博文

  随着自己工作的进行,接触到的技术栈也越来越多。给我一个很直观的感受就是,某一项技术/经验在刚开始接触的时候都记得很清楚。往往过了几个月都会忘记的差不多了,只有经常会用到的东西才有可能真正记下来。存在很多在特殊情况下有一点用处的技巧,用的不多的技巧可能一个星期就忘了。

  想了很久想通过一些手段把这些事情记录下来。也尝试过在书上记笔记,这也只是一时的,书不在手边的时候那些笔记就和没记一样,不是很方便。

  很多时候我们遇到了问题,一般情况下都是选择在搜索引擎检索相关内容,这样来的也更快一点,除非真的找不到才会去选择翻书。后来就想到了写博客,博客作为自己的一个笔记平台倒是挺合适的。随时可以查阅,不用随身携带。

  同时由于写博客是对外的,既然是对外的就不能随便写,任何人都可以看到。经验对于我来说那就只是经验而已,公布出来说不一定我的一些经验可以帮助到其他的人。遇到和我相同问题时可以少走一些弯路。

  既然决定了要写博客,那就只能认真去写。不管写的好不好,尽力就行。千里之行始于足下,一步一个脚印,慢慢来 ,写的多了慢慢也会变好的。权当是记录自己的成长的一个过程,等到以后再往回看时,就会发现自己以前原来这么菜😂。

  本系列博客所述资料均来自互联网,并不是本人原创(只有博客是自己写的)。出于热心,本人将自己的所学笔记整理并推出相对应的使用教程,方面其他人学习。为国内的物联网事业发展尽自己的一份绵薄之力,没有为自己谋取私利的想法。若出现侵权现象,请告知本人,本人会立即停止更新,并删除相应的文章和代码。

前置知识

介绍

  configfs是 Linux 内核中的一种虚拟文件系统,用于动态配置内核对象的属性和行为。它提供了一种用户空间和内核空间之间交互的接口,允许用户空间通过创建和管理 configfs 目录来配置内核对象。

  configfs 的设计目标是提供一种灵活、可扩展的配置机制,使用户能够在运行时动态地配置内核对象,而无需重新编译和加载内核模块。它可以与其他子系统(如网络子系统、文件系统等)进行集成,以实现更复杂的配置和管理功能。

支持列表

  configfs可以与其他子系统(如网络子系统、文件系统、USB等)进行集成,以实现更复杂的配置和管理功能。通过与其他子系统的集成,configfs 可以提供更丰富的配置选项和更灵活的配置方式。被支持的设备都会以普通文件/文件夹的形式创建在/sys/kernel/config中。这点和sysfs很相似。

root@sdxlemur:/sys/kernel/config# ls
stp-policy  usb_gadget
读写操作

  configfs支持属性和操作。属性可以是简单的键值对形式,用于表示内核对象的状态和配置信息。操作则是一组函数指针,用于执行内核对象的特定操作。用户空间可以通过读取和写入 configfs 文件来访问和修改内核对象的属性,还可以通过调用 configfs 文件来执行内核对象的操作。

  以usb_gadget为例,示例中通过往UDC中写入none控制usb断开,往UDC中写入数据a600000.dwc3控制usb的连接。写入的数据是通过cat指令事先查询出来的。

root@sdxlemur:/sys/kernel/config/usb_gadget/g1# cat ./UDC
a600000.dwc3
root@sdxlemur:/sys/kernel/config/usb_gadget/g1# echo none > ./UDC
root@sdxlemur:/sys/kernel/config/usb_gadget/g1# dmesg -c
[  289.275213] msm-dwc3 a600000.ssusb: ep [gsi-epin1,29] already configured as msm endpoint
[  289.275252] msm-dwc3 a600000.ssusb: ep [gsi-epout1,30] already configured as msm endpoint
[  289.381579] android_work: sent uevent USB_STATE=DISCONNECTED
[  289.400634] systemd-udevd[1511]: android0: Process '/etc/udev/scripts/automtp.sh' failed with exit code 1.
[  289.480029] msm-dwc3 a600000.ssusb: Could not get usb psy
[  289.575063] msm-dwc3 a600000.ssusb: Could not get usb psy
[  289.575114] android_work: did not send uevent (0 0 00000000)
root@sdxlemur:/sys/kernel/config/usb_gadget/g1# echo a600000.dwc3 > ./UDC
root@sdxlemur:/sys/kernel/config/usb_gadget/g1# dmesg -c
[  297.674319] android_work: sent uevent USB_STATE=CONNECTED
[  297.697035] systemd-udevd[1511]: android0: Process '/etc/udev/scripts/automtp.sh' failed with exit code 1.
[  297.706357] configfs-gadget gadget: high-speed config #1: c
[  297.712878] msm-dwc3 a600000.ssusb: Could not get usb psy
[  297.712973] android_work: sent uevent USB_STATE=CONFIGURED
[  297.742445] ipa_usb_notify_cb: Set net_ready_trigger
[  297.751615] systemd-udevd[1514]: android0: Process '/etc/udev/scripts/automtp.sh' failed with exit code 1.

  configfs除了可以修改配置中的数据,还可以通过创建和删除 configfs 目录来实现对内核对象的动态配置。同样以usb_gadget为例,该目录下的每一个文件夹都代表了USB的一个配置。我们可以通过mkdir创建一个新的配置g2。创建一个配置目录后,我们可以发现,创建的配置目录中已经存在了很多的配置项。配置目录在创建时实际是按照特定的模板去进行创建的,模板由驱动设置。

root@sdxlemur:/sys/kernel/config/usb_gadget# ls
g1
root@sdxlemur:/sys/kernel/config/usb_gadget# mkdir g2
root@sdxlemur:/sys/kernel/config/usb_gadget# ls -l
-rw-rw-r--    1 root     system        4096 Jan  1 02:55 UDC
-rw-r--r--    1 root     root          4096 Jan  1 05:27 bDeviceClass
-rw-r--r--    1 root     root          4096 Jan  1 05:27 bDeviceProtocol
-rw-r--r--    1 root     root          4096 Jan  1 05:27 bDeviceSubClass
-rw-r--r--    1 root     root          4096 Jan  1 05:27 bMaxPacketSize0
-rw-r--r--    1 root     root          4096 Jan  1 05:27 bcdDevice
-rw-r--r--    1 root     root          4096 Jan  1 05:27 bcdUSB
drwxr-xr-x    3 root     root             0 Jan  1 02:51 configs
-rw-r--r--    1 root     root          4096 Jan  1 05:27 driver_match_existing_only
drwxr-xr-x   25 root     root             0 Jan  1 02:51 functions
-rw-r--r--    1 root     root          4096 Jan  1 02:51 idProduct
-rw-r--r--    1 root     root          4096 Jan  1 02:51 idVendor
-rw-r--r--    1 root     root          4096 Jan  1 05:27 max_speed
drwxr-xr-x    2 root     root             0 Jan  1 02:51 os_desc
drwxr-xr-x    3 root     root             0 Jan  1 02:51 strings
root@sdxlemur:/sys/kernel/config/usb_gadget# rmdir ./g2
root@sdxlemur:/sys/kernel/config/usb_gadget# ls
g1

  需要注意的是,configfs 的具体实现和用法可能会因内核版本和具体的配置需求而有所不同。在使用时,可以参考相关文档和示例来了解具体的使用方法。

添加配置

  针对上面示例usb_gadget模版中的文件,其对应的注册方法位于/kernel/msm-5.4/drivers/usb/gadget/configfs.c文件中。如果需要新增一个配置文件,也是按照该方法添加一个声明。最后在编写处理函数即可。

CONFIGFS_ATTR(gadget_dev_desc_, bDeviceClass);
CONFIGFS_ATTR(gadget_dev_desc_, bDeviceSubClass);
CONFIGFS_ATTR(gadget_dev_desc_, bDeviceProtocol);
CONFIGFS_ATTR(gadget_dev_desc_, bMaxPacketSize0);
CONFIGFS_ATTR(gadget_dev_desc_, idVendor);
CONFIGFS_ATTR(gadget_dev_desc_, idProduct);
CONFIGFS_ATTR(gadget_dev_desc_, bcdDevice);
CONFIGFS_ATTR(gadget_dev_desc_, bcdUSB);
CONFIGFS_ATTR(gadget_dev_desc_, UDC);
CONFIGFS_ATTR(gadget_dev_desc_, max_speed);
CONFIGFS_ATTR(gadget_, driver_match_existing_only);

  关于处理函数,我们选择max_speed作为示例,在用户空间中通过configfs读取max_speed文件就等同于调用gadget_dev_desc_max_speed_show函数,修改max_speed文件等同于调用gadget_dev_desc_max_speed_store函数。

static ssize_t gadget_dev_desc_max_speed_show(struct config_item *item,char *page)
{
	enum usb_device_speed speed = to_gadget_info(item)->composite.max_speed;
	return scnprintf(page, PAGE_SIZE, "%s\n", usb_speed_string(speed));
}
static ssize_t gadget_dev_desc_max_speed_store(struct config_item *item,const char *page, size_t len)
{
	struct gadget_info *gi = to_gadget_info(item);
	mutex_lock(&gi->lock);
	/* Prevent changing of max_speed after the driver is binded */
	if (gi->composite.gadget_driver.udc_name)
		goto err;
	if (strncmp(page, "super-speed-plus", 16) == 0)
		gi->composite.max_speed = USB_SPEED_SUPER_PLUS;
	else if (strncmp(page, "super-speed", 11) == 0)
		gi->composite.max_speed = USB_SPEED_SUPER;
	else if (strncmp(page, "high-speed", 10) == 0)
		gi->composite.max_speed = USB_SPEED_HIGH;
	else if (strncmp(page, "full-speed", 10) == 0)
		gi->composite.max_speed = USB_SPEED_FULL;
	else if (strncmp(page, "low-speed", 9) == 0)
		gi->composite.max_speed = USB_SPEED_LOW;
	else
		goto err;
	gi->composite.gadget_driver.max_speed = gi->composite.max_speed;
	mutex_unlock(&gi->lock);
	return len;
err:
	mutex_unlock(&gi->lock);
	return -EINVAL;
}

  文件夹则是通过config_group_init_type_name函数进行创建。

	config_group_init_type_name(&gi->group, name, &gadget_root_type);
	config_group_init_type_name(&gi->group, name, &gadget_root_type);
	config_group_init_type_name(&gi->functions_group, "functions",&functions_type);
	configfs_add_default_group(&gi->functions_group, &gi->group);
	config_group_init_type_name(&gi->configs_group, "configs",&config_desc_type);
	configfs_add_default_group(&gi->configs_group, &gi->group);
	config_group_init_type_name(&gi->strings_group, "strings",&gadget_strings_strings_type);
	configfs_add_default_group(&gi->strings_group, &gi->group);
	config_group_init_type_name(&gi->os_desc_group, "os_desc",&os_desc_type);
	configfs_add_default_group(&gi->os_desc_group, &gi->group);
	xxxxx
}

代码

文件和函数的关系

  展开CONFIGFS_ATTR宏,可以看到内核就是通过宏定义了一个configfs_attribute 结构体,在结构体中分别通过.show和.store定义了对应的处理函数。

// kernel/msm-5.4/include/linux/configfs.h
#define CONFIGFS_ATTR(_pfx, _name)			\
static struct configfs_attribute _pfx##attr_##_name = {	\
	.ca_name	= __stringify(_name),		\
	.ca_mode	= S_IRUGO | S_IWUSR,		\
	.ca_owner	= THIS_MODULE,			\
	.show		= _pfx##_name##_show,		\
	.store		= _pfx##_name##_store,		\
}

  同样以CONFIGFS_ATTR(gadget_dev_desc_, max_speed);为例,_pfx等于gadget_dev_desc_,_name等于max_speed。那么最后被定义的结构体_pfx##attr_##_name展开后就是gadget_dev_desc_attr_max_speed。 结构体中的.show成员 _pfx##_name##_show被展开后就是gadget_dev_desc_max_speed_show。

  gadget_dev_desc_attr_max_speed结构体将configfs机制下的文件和其对应操作函数进行了绑定,通过层层封装,最终存放在gadget_root_type 中。gadget_root_type 将所有的configfs文件整合在一起作为一个模板。在创建配置时将被使用到。

static struct configfs_attribute *gadget_root_attrs[] = {
	xxxxxxxxxxx
	&gadget_dev_desc_attr_max_speed,
	xxxxxxxxxxxx
	NULL,
};
static const struct config_item_type gadget_root_type = {
	.ct_item_ops = &gadget_root_item_ops,
	.ct_attrs = gadget_root_attrs,
	.ct_owner = THIS_MODULE,
};
创建对象

  在configfs的框架里使用configfs_group_operations 去定义mkdir和rmdir的行为。上面示例在用户空间中执行了mkdir g2等同于直接调用gadgets_make(NULL, "g2");的接口。

static struct configfs_group_operations gadgets_ops = {
	.make_group = &gadgets_make,
	.drop_item = &gadgets_drop,
};

  在gadgets_make函数中可以清晰地看到gadget_root_type模板被使用,用于在创建对象时,同步创建模板中记录的全部文件。随后便创建了配置中的文件夹。

static struct config_group *gadgets_make(struct config_group *group,const char *name)
{
	struct gadget_info *gi;
	gi = kzalloc(sizeof(*gi), GFP_KERNEL);
	if (!gi)
		return ERR_PTR(-ENOMEM);
	config_group_init_type_name(&gi->group, name, &gadget_root_type);
	config_group_init_type_name(&gi->group, name, &gadget_root_type);
	config_group_init_type_name(&gi->functions_group, "functions",&functions_type);
	configfs_add_default_group(&gi->functions_group, &gi->group);
	config_group_init_type_name(&gi->configs_group, "configs",&config_desc_type);
	configfs_add_default_group(&gi->configs_group, &gi->group);
	config_group_init_type_name(&gi->strings_group, "strings",&gadget_strings_strings_type);
	configfs_add_default_group(&gi->strings_group, &gi->group);
	config_group_init_type_name(&gi->os_desc_group, "os_desc",&os_desc_type);
	configfs_add_default_group(&gi->os_desc_group, &gi->group);
	xxxxx
}

小结

  configfs是基于文件系统的内核对象管理器,在用户空间通过mkdir显式的创建,使用rmdir销毁。

  在mkdir之后会出现对应的属性。用户程序可以通过 readdir、read 函数读取这些属性,也可以通过 write 函数修改某些属性。重点在于configfs中的内容是在用户空间里创建、销毁,用户空间控制着 configfs的生命周期。可以认为 configfs就是这些内核对象的管理器。

  那么本篇博客就到此结束了,这里只是记录了一些我个人的学习笔记,其中存在大量我自己的理解。文中所述不一定是完全正确的,可能有的地方我自己也理解错了。如果有些错的地方,欢迎大家批评指正。如有问题直接在对应的博客评论区指出即可,不需要私聊我。我们交流的内容留下来也有助于其他人查看,说不一定也有其他人遇到了同样的问题呢😂。

转载请注明来自码农世界,本文标题:《Linux文件系统:2、configfs详解》

百度分享代码,如果开启HTTPS请参考李洋个人博客
每一天,每一秒,你所做的决定都会改变你的人生!

发表评论

快捷回复:

评论列表 (暂无评论,66人围观)参与讨论

还没有评论,来说两句吧...

Top