本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,并注明原作者及原链接。内容可任意使用,但对因使用该内容引起的后果不做任何保证。
作者:fireaxe.hq@outlook.com
博客:fireaxe.blog.chinaunix.net
本文基于linux 4.x代码,对sysfs的基本概念、实现原理进行了介绍;结合cpufreq分析了sysfs的使用方法;最后,通过一个例子对全文进行了总结。
参考文档:
1. 基本概念
sysfs 给应用程序提供了统一访问设备的接口,但可以看到, sysfs 仅仅是提供了一个可以统一访问设备的框架,但究竟是否支持 sysfs 还需要各设备驱动程序的编程支持;在 2.6 内核诞生 5年以来的发展中,很多子系统、设备驱动程序逐渐转向了 sysfs 作为与用户空间友好的接口,但仍然也存在大量的代码还在使用旧的 proc 或虚拟字符设备的 ioctl 方式;如果仅从最终用户的角度来说, sysfs 与 proc 都是在提供相同或类似的功能,对于旧的 proc 代码,没有绝对的必要去做 proc 至 sysfs 的升级;因此在可预见的将来, sysfs 会与 proc, debugfs, configfs 等共存很长一段时间。
sysfs 虚拟文件系统提供了一种比 proc 更为理想的访问内核数据的途径
sysfs 文件系统总是被挂载在 /sys 挂载点上。虽然在较早期的2.6内核系统上并没有规定 sysfs 的标准挂载位置,可以把 sysfs 挂载在任何位置,但较近的2.6内核修正了这一规则,要求 sysfs 总是挂载在 /sys 目录上;针对以前的 sysfs 挂载位置不固定或没有标准被挂载,有些程序从 /proc/mounts 中解析出 sysfs 是否被挂载以及具体的挂载点,这个步骤现在已经不需要了。
sysfs 与 proc 相比有很多优点,最重要的莫过于设计上的清晰。一个 proc 虚拟文件可能有内部格式,如 /proc/scsi/scsi ,它是可读可写的,(其文件权限被错误地标记为了 0444 !,这是内核的一个BUG),并且读写格式不一样,代表不同的操作,应用程序中读到了这个文件的内容一般还需要进行字符串解析,而在写入时需要先用字符串格式化按指定的格式写入字符串进行操作;相比而言, sysfs 的设计原则是一个属性文件只做一件事情, sysfs 属性文件一般只有一个值,直接读取或写入。整个 /proc/scsi 目录在2.6内核中已被标记为过时(LEGACY),它的功能已经被相应的 /sys 属性文件所完全取代。新设计的内核机制应该尽量使用 sysfs 机制,而将 proc 保留给纯净的“进程文件系统”。
2. sysfs目录结构
/sys 下的目录结构是经过精心设计的:在 /sys/devices 下是所有设备的真实对象,包括如视频卡和以太网卡等真实的设备,也包括 ACPI 等不那么显而易见的真实设备、还有 tty, bonding 等纯粹虚拟的设备;在其它目录如 class, bus 等中则在分类的目录中含有大量对 devices 中真实对象引用的符号链接文件。
/sys 下的子目录
|
所包含的内容
|
/sys/devices
|
这是内核对系统中所有设备的分层次表达模型,也是 /sys 文件系统管理设备的最重要的目录结构,下文会对它的内部结构作进一步分析;
|
/sys/dev
|
这个目录下维护一个按字符设备和块设备的主次号码(major:minor)链接到真实的设备(/sys/devices下)的符号链接文件,它是在内核 2.6.26 首次引入;
|
/sys/bus
|
这是内核设备按总线类型分层放置的目录结构, devices 中的所有设备都是连接于某种总线之下,在这里的每一种具体总线之下可以找到每一个具体设备的符号链接,它也是构成 Linux 统一设备模型的一部分;
|
/sys/class
|
这是按照设备功能分类的设备模型,如系统所有输入设备都会出现在 /sys/class/input 之下,而不论它们是以何种总线连接到系统。它也是构成 Linux 统一设备模型的一部分;
|
/sys/block
|
这里是系统中当前所有的块设备所在,按照功能来说放置在 /sys/class 之下会更合适,但只是由于历史遗留因素而一直存在于 /sys/block, 但从 2.6.22 开始就已标记为过时,只有在打开了 CONFIG_SYSFS_DEPRECATED 配置下编译才会有这个目录的存在,并且在 2.6.26 内核中已正式移到 /sys/class/block, 旧的接口 /sys/block 为了向后兼容保留存在,但其中的内容已经变为指向它们在 /sys/devices/ 中真实设备的符号链接文件;
|
/sys/firmware
|
这里是系统加载固件机制的对用户空间的接口,关于固件有专用于固件加载的一套API,在附录 LDD3 一书中有关于内核支持固件加载机制的更详细的介绍;
|
/sys/fs
|
这里按照设计是用于描述系统中所有文件系统,包括文件系统本身和按文件系统分类存放的已挂载点,但目前只有 fuse,ext4 等少数文件系统支持 sysfs 接口,一些传统的虚拟文件系统(VFS)层次控制参数仍然在 sysctl (/proc/sys/fs) 接口中;
|
/sys/kernel
|
这里是内核所有可调整参数的位置,有些内核可调整参数仍然位于 sysctl (/proc/sys/kernel) 接口中 ;
|
/sys/module
|
这里有系统中所有模块的信息,不论这些模块是以内联(inlined)方式编译到内核映像文件(vmlinuz)中还是编译为外部模块(ko文件),都可能会出现在 /sys/module 中: 编译为外部模块(ko文件)在加载后会出现对应的 /sys/module//, 并且在这个目录下会出现一些属性文件和属性目录来表示此外部模块的一些信息,如版本号、加载状态、所提供的驱动程序等; 编译为内联方式的模块则只在当它有非0属性的模块参数时会出现对应的 /sys/module/, 这些模块的可用参数会出现在 /sys/modules//parameters/ 中, 如 /sys/module/printk/parameters/time 这个可读写参数控制着内联模块 printk 在打印内核消息时是否加上时间前缀; 所有内联模块的参数也可以由 ".=" 的形式写在内核启动参数上,如启动内核时加上参数 "printk.time=1" 与 向 "/sys/module/printk/parameters/time" 写入1的效果相同; 没有非0属性参数的内联模块不会出现于此。
|
/sys/power
|
这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机、重启等。
|
接下来对 /sys/devices/ 下的目录结构作进一步探讨:
清单 2. 查看 /sys/devices/ 的目录结构
1 2
|
$ ls -F /sys/devices/ isa/ LNXSYSTM:00/ pci0000:00/ platform/ pnp0/ pnp1/ system/ virtual/
|
可以看到,在 /sys/devices/ 目录下是按照设备的基本总线类型分类的目录,再进入进去查看其中的 PCI 类型的设备:
清单 3. 查看 /sys/devices/pci0000:00/ 的目录结构
1 2 3 4
|
$ ls -F /sys/devices/pci0000:00/ 0000:00:00.0/ 0000:00:02.5/ 0000:00:03.1/ 0000:00:0e.0/ power/ 0000:00:01.0/ 0000:00:02.7/ 0000:00:03.2/ firmware_node@ uevent 0000:00:02.0/ 0000:00:03.0/ 0000:00:03.3/ pci_bus/
|
在 /sys/devices/pci0000:00/ 目录下是按照 PCI 总线接入的设备号分类存放的目录,再查看其中一个,
清单 4. 查看 /sys/devices/pci0000:00/ 的目录结构
1 2 3 4 5
|
$ ls -F /sys/devices/pci0000:00/0000:00:01.0/ 0000:01:00.0/ device local_cpus power/ subsystem_vendor broken_parity_status enable modalias resource uevent class irq msi_bus subsystem@ vendor config local_cpulist pci_bus/ subsystem_device
|
可以看到,其中有一个目录 0000:01:00.0/, 其它都是属性文件和属性组,而如果对 0000:01:00.0/ 子目录中进行再列表查看则会得到 清单 1 的目录结构。
继续以上过程可以了解整个目录树的结构,这里把它整理成 图 1. sysfs 目录层次
其中涉及到 ksets, kobjects, attrs 等很多术语,这就不得不提到 Linux 统一设备模型。
3. 从Linux 统一设备模型角度看sysfs
在 Linux 2.5 内核的开发过程中,人们设计了一套新的设备模型,目的是为了对计算机上的所有设备进行统一地表示和操作,包括设备本身和设备之间的连接关系。这个模型是在分析了 PCI 和 USB 的总线驱动过程中得到的,这两个总线类型能代表当前系统中的大多数设备类型,它们都有完善的热挺拔机制和电源管理的支持,也都有级连机制的支持,以桥接的 PCI/USB 总线控制器的方式可以支持更多的 PCI/USB 设备。为了给所有设备添加统一的电源管理的支持,而不是让每个设备中去独立实现电源管理的支持,人们考虑的是如何尽可能地重用代码;而且在有层次模型的 PCI/USB 总线中,必须以合理形式展示出这个层次关系,这也是电源管理等所要求的必须有层次结构。
如在一个典型的 PC 系统中,中央处理器(CPU)能直接控制的是 PCI 总线设备,而 USB 总线设备是以一个 PCI 设备(PCI-USB桥)的形式接入在 PCI 总线设备上,外部 USB 设备再接入在 USB 总线设备上;当计算机执行挂起(suspend)操作时, Linux 内核应该以 “外部USB设备->USB总线设备->PCI总线设备” 的顺序通知每一个设备将电源挂起;执行恢复(resume)时则以相反的顺序通知;反之如果不按此顺序则将有设备得不到正确的电源状态变迁的通知,将无法正常工作。
sysfs 是在这个 Linux 统一设备模型的开发过程中的一项副产品(见 参考资料 中 Greg K. Hartman 写作的 LinuxJournal 文章)。为了将这些有层次结构的设备以用户程序可见的方式表达出来,人们很自然想到了利用文件系统的目录树结构(这是以 UNIX 方式思考问题的基础,一切都是文件!)在这个模型中,有几种基本类型,它们的对应关系见 表 2. Linux 统一设备模型的基本结构 :
表 2. Linux 统一设备模型的基本结构
类型
|
所包含的内容
|
对应内核数据结构
|
对应/sys项
|
设备(Devices)
|
设备是此模型中最基本的类型,以设备本身的连接按层次组织
|
struct device
|
/sys/devices/*/*/.../
|
设备驱动(Device Drivers)
|
在一个系统中安装多个相同设备,只需要一份驱动程序的支持
|
struct device_driver
|
/sys/bus/pci/drivers/*/
|
总线类型(Bus Types)
|
在整个总线级别对此总线上连接的所有设备进行管理
|
struct bus_type
|
/sys/bus/*/
|
设备类别(Device Classes)
|
这是按照功能进行分类组织的设备层次树;如 USB 接口和 PS/2 接口的鼠标都是输入设备,都会出现在 /sys/class/input/ 下
|
struct class
|
/sys/class/*/
|
从内核在实现它们时所使用的数据结构来说, Linux 统一设备模型又是以两种基本数据结构进行树型和链表型结构组织的:
-
kobject: 在 Linux 设备模型中最基本的对象,它的功能是提供引用计数和维持父子(parent)结构、平级(sibling)目录关系,上面的 device, device_driver 等各对象都是以 kobject 基础功能之上实现的;
1 2 3 4 5 67 8 9 10 11 12 13
|
struct kobject { const char *name; struct list_head entry; struct kobject *parent; struct kset *kset; struct kobj_type *ktype; struct sysfs_dirent *sd; struct kref kref; unsigned int state_initialized:1; unsigned int state_in_sysfs:1; unsigned int state_add_uevent_sent:1;unsigned int state_remove_uevent_sent:1; };
|
其中 struct kref 内含一个 atomic_t 类型用于引用计数, parent 是单个指向父节点的指针, entry 用于父 kset 以链表头结构将 kobject 结构维护成双向链表;
-
kset: 它用来对同类型对象提供一个包装集合,在内核数据结构上它也是由内嵌一个 kboject 实现,因而它同时也是一个 kobject (面向对象 OOP 概念中的继承关系) ,具有 kobject 的全部功能;
1 2 3 4 5 6
|
struct kset { struct list_head list; spinlock_t list_lock; struct kobject kobj; struct kset_uevent_ops *uevent_ops; };
|
其中的 struct list_head list 用于将集合中的 kobject 按 struct list_head entry 维护成双向链表;
涉及到文件系统实现来说, sysfs 是一种基于 ramfs 实现的内存文件系统,与其它同样以 ramfs 实现的内存文件系统(configfs,debugfs,tmpfs,...)类似, sysfs 也是直接以 VFS 中的 struct inode 和 struct dentry 等 VFS 层次的结构体直接实现文件系统中的各种对象;同时在每个文件系统的私有数据 (如 dentry->d_fsdata 等位置) 上,使用了称为 struct sysfs_dirent 的结构用于表示 /sys 中的每一个目录项。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
struct sysfs_dirent { atomic_t s_count; atomic_t s_active; struct sysfs_dirent *s_parent; struct sysfs_dirent *s_sibling; const char *s_name; union { struct sysfs_elem_dir s_dir; struct sysfs_elem_symlink s_symlink; struct sysfs_elem_attr s_attr; struct sysfs_elem_bin_attr s_bin_attr; }; unsigned int s_flags; ino_t s_ino; umode_t s_mode; struct iattr *s_iattr; };
|
在上面的 kobject 对象中可以看到有向 sysfs_dirent 的指针,因此在sysfs中是用同一种 struct sysfs_dirent 来统一设备模型中的 kset/kobject/attr/attr_group.
具体在数据结构成员上, sysfs_dirent 上有一个 union 共用体包含四种不同的结构,分别是目录、符号链接文件、属性文件、二进制属性文件;其中目录类型可以对应 kobject,在相应的 s_dir 中也有对 kobject 的指针,因此在内核数据结构, kobject 与 sysfs_dirent 是互相引用的;
有了这些概念,再来回头看 图 1. sysfs 目录层次图 所表达的 /sys 目录结构就是非常清晰明了:
-
在 /sys 根目录之下的都是 kset,它们组织了 /sys 的顶层目录视图;
-
在部分 kset 下有二级或更深层次的 kset;
-
每个 kset 目录下再包含着一个或多个 kobject,这表示一个集合所包含的 kobject 结构体;
-
在 kobject 下有属性(attrs)文件和属性组(attr_group),属性组就是组织属性的一个目录,它们一起向用户层提供了表示和操作这个 kobject 的属性特征的接口;
-
在 kobject 下还有一些符号链接文件,指向其它的 kobject,这些符号链接文件用于组织上面所说的 device, driver, bus_type, class, module 之间的关系;
-
不同类型如设备类型的、设备驱动类型的 kobject 都有不同的属性,不同驱动程序支持的 sysfs 接口也有不同的属性文件;而相同类型的设备上有很多相同的属性文件;
注意,此表内容是按照最新开发中的 2.6.28 内核的更新组织的,在附录资源如 LDD3 等位置中有提到 sysfs 中曾有一种管理对象称为 subsys (子系统对象),在最新的内核中经过重构认为它是不需要的,它的功能完全可以由 kset 代替,也就是说 sysfs 中只需要一种管理结构是 kset,一种代表具体对象的结构是 kobject,在 kobject 下再用属性文件表示这个对象所具有的属性;
4. Sysfs 编程实践 (以cpufreq为例)
上图是是cpufreq子系统中的sysfs的结构图,其实就是驱动统一模型。代码在文件driver/cpufreq/cpufreq.c中。需要明确的是,每个kobject都为一个目录,kobject通过parent指定其上层目录,kobject中的kytpe指定了该kobject目录下有那些属性文件。
(1) 创建子系统目录
首先是要创建一个kobject节点(全局变量cpufreq_global_kobject),然后通过kobject_create_add_add把它与它的parent kobject节点挂接起来。cpufreq是挂到cpu节点上的,所以这里的parent kobject是cpu子系统的kobject节点:cpu_subsys。从下面的开发可以看到,cpufreq_global_kobject在系统中作为目录cpufreq 被挂接到cpu节点上,cpu节点的路径为“/sys/devices/system/cpu”,所以cpufreq_global_kobject的路径为“/sys/devices/system/cpu/cpufreq”。
-
cpufreq_global_kobject = kobject_create_and_add("cpufreq", &cpu_subsys.dev_root->kobj);
(2) 创建policy目录
cpufreq子系统下可以挂多个policy,每个policy都是一个独立的目录。
-
kobject_init(&policy->kobj, &ktype_cpufreq);
-
-
-
kobject_add(&policy->kobj, cpufreq_global_kobject,
-
"policy%u",
-
cpumask_first(policy->related_cpus));
首先通过kobject_init创建并初始化policy->kobj,主要是初始化ktype。ktype_cpufreq是一个结构体,定义了要显示的属性文件,它的内容下一节再分析。
第二步是通过kobject_add把policy->kobj添加到cpufreq_global_kobject上,并指定目录名。我们假设要挂到cpu0上,这样创建后的路径为:“/sys/devices/system/cpu/cpufreq/policy0”。
也可以用kobject_init_add一次完成两个操作。
(3) 创建属性文件
1)cpufreq的实现方式
a) 通过数组批量创建属性文件
上面说道了属性文件是通过结构体ktype_cpufreq来添加的,这里我们就来看一下这个结构体:
-
static const struct sysfs_ops sysfs_ops = {
-
-
-
.show = show,
-
.store = store,
-
};
-
-
static struct attribute *default_attrs[] = {
-
&cpuinfo_min_freq.attr,
-
&cpuinfo_max_freq.attr,
-
&cpuinfo_transition_latency.attr,
-
&scaling_min_freq.attr,
-
&scaling_max_freq.attr,
-
&affected_cpus.attr,
-
&related_cpus.attr,
-
&scaling_governor.attr,
-
&scaling_driver.attr,
-
&scaling_available_governors.attr,
-
&scaling_setspeed.attr,
-
NULL
-
};
-
-
static struct kobj_type ktype_cpufreq = {
-
.sysfs_ops = &sysfs_ops,
-
.default_attrs = default_attrs,
-
.release = cpufreq_sysfs_release,
-
};
ktype_cpufreq中有三个成员变量:
-
release用于释放资源,一般应该不需要;
-
sysfs_ops中实现了两个函数show()与store(),对属性文件读写时调用这两个函数;
-
default_attrs中定义了要显示的属性,是一个struct attribute的指针数组。
下面以其中的scaling_setspeed为例:
-
#define cpufreq_freq_attr_rw(_name) \
-
-
-
static struct freq_attr _name = \
-
__ATTR(_name, 0644, show_##_name, store_##_name)
-
-
cpufreq_freq_attr_rw(scaling_setspeed);
上面的代码展开后为:
-
static struct freq_attr scaling_setspeed = __ATTR(scaling_setspeed, 0644, show_scaling_setspeed, store_scaling_setspeed)
__ATTR是sysfs中定义的一个宏,用于初始化freq_attr。这里是为scaling_setspeed指定了show与store两个函数,这样在访问属性文件scaling_setspeed时,会用到这两个函数。
这里有个问题,上面提到了访问ktype_cpufreq时,会使用sysfs_ops中定义的store与show,怎么这里又为每个节点添加了独立的store与show?
要解答这个问题,让我们来看看sysfs_ops中的store与show的实现:
-
static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
-
-
-
{
-
struct cpufreq_policy *policy = to_policy(kobj);
-
struct freq_attr *fattr = to_attr(attr);
-
ssize_t ret;
-
-
down_read(&policy->rwsem);
-
-
if (fattr->show)
-
ret = fattr->show(policy, buf);
-
else
-
ret = -EIO;
-
-
up_read(&policy->rwsem);
-
-
return ret;
-
}
-
-
static ssize_t store(struct kobject *kobj, struct attribute *attr,
-
const char *buf, size_t count)
-
{
-
struct cpufreq_policy *policy = to_policy(kobj);
-
struct freq_attr *fattr = to_attr(attr);
-
ssize_t ret = -EINVAL;
-
-
get_online_cpus();
-
-
if (!cpu_online(policy->cpu))
-
goto unlock;
-
-
down_write(&policy->rwsem);
-
-
if (fattr->store)
-
ret = fattr->store(policy, buf, count);
-
else
-
ret = -EIO;
-
-
up_write(&policy->rwsem);
-
unlock:
-
put_online_cpus();
-
-
return ret;
-
}
从上面的代码可以看到,访问policy->kobj时,确实用的是sysfs_ops的store与show,但cpufreq扩展了attribute为freq_attr,用于添加每个属性的show与store函数。顶层的show与store会根据属性的不同,再去调用对应属性的show与store函数。也就是说这个是cpufreq驱动自己的行为,与sysfs无关,当然根据sysfs的实现,应该在sysfs_ops的store与show中通过区分属性名而分别调用不同的行为,一个函数去搞定。
b) 单独创建创建属性文件
第一种通过数组attribute创建一组属性文件的优势是方便,劣势是不灵活。比如,某些属性需要根据系统信息决定是否支持。例如,cpufreq中有些支持cpu支持setspeed,有些不支持。这就需要根据cpu类型决定是否创建setspeed属性文件。显然,需要单独的接口来创建单个的属性文件,然后通过判断cpu类型决定是否调用该接口。
为了找到这些接口,首先我们来深入到kobject_add来看一看属性文件最终是如何创建的。
顺着kobject_add去看,很容易就可以找到create_dir,代码如下:
-
static int create_dir(struct kobject *kobj)
-
-
-
{
-
const struct kobj_ns_type_operations *ops;
-
int error;
-
-
error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj));
-
if (error)
-
return error;
-
-
error = populate_dir(kobj);
-
if (error) {
-
sysfs_remove_dir(kobj);
-
return error;
-
}
-
.....
-
}
-
-
static int populate_dir(struct kobject *kobj)
-
{
-
......
-
if (t && t->default_attrs) {
-
for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
-
error = sysfs_create_file(kobj, attr);
-
if (error)
-
break;
-
}
-
}
-
return error;
-
}
显然,是通过sysfs_create_****这一组接口最终实现的创建sysfs目录与文件。
在include/linux/sysfs.h中,提供了一组接口:
-
int __must_check sysfs_create_dir_ns(struct kobject *kobj, const void *ns);
-
-
-
int __must_check sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, const void *new_ns);
-
int __must_check sysfs_create_mount_point(struct kobject *parent_kobj, const char *name);
-
int __must_check sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr, const void *ns);
-
int __must_check sysfs_create_files(struct kobject *kobj, const struct attribute **attr);
-
int __must_check sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr, umode_t mode);
-
int __must_check sysfs_create_bin_file(struct kobject *kobj, const struct bin_attribute *attr);
-
int __must_check sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name);
2)仿照cpufreq自己写一个
在/sys/devices/system/cpu下创建了一个wicca目录,目录结构如下:
wicca
|-- test1
|-- scaling_limit_min
|-- scaling_limit_max
|-- scaling_get_sqrt
|-- scaling_get_factorial
`-- scaling_set_value
可以通过scaling_set_value设置要计算的value,通过scaling_get_sqrt读出其平凡根(因为在kernel中,只能使用int_sqrt,因此结果为整数)。
通过limit_min与limit_max设置set_value的范围。
scaling_get_factorial通过sysfs_create_file创建,其他属性通过attribute数组创建。
-
#ifndef TEST_SYSFS_H_H
-
-
-
#define TEST_SYSFS_H_H
-
-
#include <linux/module.h> /* for modules */
-
#include <linux/blkdev.h>
-
#include <linux/mount.h>
-
#include <linux/fsnotify.h>
-
#include <linux/fs.h> /* file_operations */
-
#include <linux/uaccess.h> /* copy_(to,from)_user */
-
#include <linux/init.h> /* module_init, module_exit */
-
#include <linux/slab.h> /* kmalloc, kfree */
-
#include <linux/device.h>
-
#include <linux/cdev.h>
-
#include <linux/io.h>
-
#include <linux/debugfs.h>
-
#include <linux/mm.h>
-
#include <asm/uaccess.h>
-
#include <linux/cpu.h>
-
-
struct wicca_sysfs
-
{
-
struct kobject kobj;
-
struct rw_semaphore rwsem;
-
u32 limit_min;
-
u32 limit_max;
-
u32 value;
-
};
-
-
struct test1_attr {
-
struct attribute attr;
-
ssize_t (*show)(struct wicca_sysfs*, char *);
-
ssize_t (*store)(struct wicca_sysfs*, const char *, size_t count);
-
};
-
-
#define wicca_attr_ro(_name) \
-
static struct test1_attr _name = \
-
__ATTR(_name, 0444, show_##_name, NULL)
-
-
#define wicca_attr_ro_perm(_name, _perm) \
-
static struct test1_attr _name = \
-
__ATTR(_name, _perm, show_##_name, NULL)
-
-
#define wicca_attr_rw(_name) \
-
static struct test1_attr _name = \
-
__ATTR(_name, 0644, show_##_name, store_##_name)
-
-
#endif /* TEST_SYSFS_H_H */
-
/** test_sysfs module
-
-
-
* this module shall be installed into kernel
-
*/
-
-
#include "test_sysfs.h"
-
-
static ssize_t store_scaling_limit_min(struct wicca_sysfs *wicca, const char *buf, size_t count)
-
{
-
int ret, temp;
-
printk("calling %s\n", __func__);
-
-
ret = sscanf(buf, "%u", &temp);
-
if (ret != 1)
-
{
-
return -EINVAL;
-
}
-
-
if ((temp < wicca->limit_max) && (temp <= 10))
-
{
-
wicca->limit_min = temp;
-
}
-
-
return count;
-
}
-
-
static ssize_t show_scaling_limit_min(struct wicca_sysfs *wicca, char *buf)
-
{
-
printk("calling %s\n", __func__);
-
-
return sprintf(buf, "%u\n", wicca->limit_min);
-
}
-
-
-
static ssize_t store_scaling_limit_max(struct wicca_sysfs *wicca, const char *buf, size_t count)
-
{
-
int ret, temp;
-
printk("calling %s\n", __func__);
-
-
ret = sscanf(buf, "%u", &temp);
-
if (ret != 1)
-
{
-
return -EINVAL;
-
}
-
-
if ((temp > wicca->limit_min) && (temp <= 20))
-
{
-
wicca->limit_max = temp;
-
}
-
-
return count;
-
}
-
-
static ssize_t show_scaling_limit_max(struct wicca_sysfs *wicca, char *buf)
-
{
-
printk("calling %s\n", __func__);
-
return sprintf(buf, "%u\n", wicca->limit_max);
-
}
-
-
static ssize_t store_scaling_set_value(struct wicca_sysfs *wicca, const char *buf, size_t count)
-
{
-
int ret, temp;
-
printk("calling %s\n", __func__);
-
-
ret = sscanf(buf, "%u", &temp);
-
if (ret != 1)
-
{
-
return -EINVAL;
-
}
-
-
if ((temp <= wicca->limit_max) && (temp >= wicca->limit_min))
-
{
-
wicca->value = temp;
-
}
-
-
-
return count;
-
}
-
-
static ssize_t show_scaling_set_value(struct wicca_sysfs *wicca, char *buf)
-
{
-
printk("calling %s\n", __func__);
-
return sprintf(buf, "%u\n", wicca->value);
-
}
-
-
static ssize_t show_scaling_get_sqrt(struct wicca_sysfs *wicca, char *buf)
-
{
-
printk("calling %s\n", __func__);
-
return sprintf(buf, "%lu\n", int_sqrt(wicca->value));
-
}
-
-
static ssize_t show_scaling_get_factorial(struct wicca_sysfs *wicca, char *buf)
-
{
-
u32 tmp;
-
u32 i;
-
printk("calling %s\n", __func__);
-
-
for (i=0, tmp=1; i<wicca->value; i++)
-
{
-
tmp *= (i+1);
-
}
-
return sprintf(buf, "%u\n", tmp);
-
}
-
-
wicca_attr_rw(scaling_limit_min);
-
wicca_attr_rw(scaling_limit_max);
-
wicca_attr_rw(scaling_set_value);
-
wicca_attr_ro(scaling_get_sqrt);
-
wicca_attr_ro(scaling_get_factorial);
-
-
static struct attribute *default_attrs[] = {
-
&scaling_limit_min.attr,
-
&scaling_limit_max.attr,
-
&scaling_set_value.attr,
-
&scaling_get_sqrt.attr,
-
NULL
-
};
-
-
#define to_test1(k) container_of(k, struct wicca_sysfs, kobj)
-
#define to_attr(a) container_of(a, struct test1_attr, attr)
-
-
static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
-
{
-
struct wicca_sysfs *test1 = to_test1(kobj);
-
struct test1_attr *fattr = to_attr(attr);
-
ssize_t ret;
-
-
down_read(&test1->rwsem);
-
ret = fattr->show(test1, buf);
-
up_read(&test1->rwsem);
-
-
return ret;
-
}
-
-
static ssize_t store(struct kobject *kobj, struct attribute *attr,
-
const char *buf, size_t count)
-
{
-
struct wicca_sysfs *test1 = to_test1(kobj);
-
struct test1_attr *fattr = to_attr(attr);
-
ssize_t ret = -EINVAL;
-
-
get_online_cpus();
-
-
down_write(&test1->rwsem);
-
ret = fattr->store(test1, buf, count);
-
up_write(&test1->rwsem);
-
-
put_online_cpus();
-
-
return ret;
-
}
-
-
static struct sysfs_ops sysfs_ops = {
-
.show = show,
-
.store = store,
-
};
-
-
static struct kobj_type ktype_test1 = {
-
.sysfs_ops = &sysfs_ops,
-
.default_attrs = default_attrs,
-
};
-
-
struct kobject *wicca_global_kobject;
-
EXPORT_SYMBOL(wicca_global_kobject);
-
-
static int __init sysfs_demon_init(void)
-
{
-
int ret;
-
struct wicca_sysfs *test1;
-
-
wicca_global_kobject = kobject_create_and_add("wicca", &cpu_subsys.dev_root->kobj);
-
BUG_ON(!wicca_global_kobject);
-
-
test1 = kzalloc(sizeof(*test1), GFP_KERNEL);
-
if (!test1)
-
{
-
return -1;
-
}
-
-
ret = kobject_init_and_add(&test1->kobj, &ktype_test1,
-
wicca_global_kobject, "test%d", 1);
-
if (ret)
-
{
-
return -1;
-
}
-
-
init_rwsem(&test1->rwsem);
-
test1->limit_min = 1;
-
test1->limit_max = 10;
-
test1->value = 3;
-
-
ret = sysfs_create_file(&test1->kobj, &scaling_get_factorial.attr);
-
if (ret)
-
{
-
return -1;
-
}
-
-
return ret;
-
}
-
-
static void __exit sysfs_demon_exit(void)
-
{
-
pr_debug("wicca sysfs exit");
-
}
-
-
module_init(sysfs_demon_init);
-
module_exit(sysfs_demon_exit);
-
-
MODULE_LICENSE("GPL");
-
MODULE_AUTHOR("Marco Hao");
本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,并注明原作者及原链接。内容可任意使用,但对因使用该内容引起的后果不做任何保证。
作者:fireaxe.hq@outlook.com
博客:fireaxe.blog.chinaunix.net