Linux sysfs I

kernel管理的设备千差万别,但最终都归于统一设备模型的管理, 即sysfs,理解了sysfs,就可以管窥蠡豹,对整个DeviceDriver子系统有一个清晰的认识。sysfs的实质是以文件系统的形式展示内核中的Device Driver信息,因此,理解sysfs可以分为2个维度的问题:

  • sysfs的Inside Tree的组织思想
  • sysfs的Outside Tree的目录结构

本文主要讨论内部树的基础,kobject,kset和ktype的定义联系及其常用API

kobject

list_head是内核所有链式存储数据结构的基础,可以看作这些struct的父类,基于同样的思想,在Device Driver Subsystem中, 这个全局父类的角色由kobject担任,当然,kobject本身也是一个list_head的子类。kobject为Device Driver管理的对象提供了最高层次的抽象,无论是Device对象还是Driver对象,都可以看作kobject的父类,sysfs通过kobject,就可以管理系统中所有的Device对象和Driver对象。

//include/linux/kobject.h
 63 struct kobject { 
 64         const char              *name;
 65         struct list_head        entry;
 66         struct kobject          *parent;
 67         struct kset             *kset;
 68         struct kobj_type        *ktype;
 69         struct kernfs_node      *sd;
 70         struct kref             kref;
 71 #ifdef CONFIG_DEBUG_KOBJECT_RELEASE
 72         struct delayed_work     release;
 73 #endif
 74         unsigned int state_initialized:1;
 75         unsigned int state_in_sysfs:1;
 76         unsigned int state_add_uevent_sent:1;
 77         unsigned int state_remove_uevent_sent:1;
 78         unsigned int uevent_suppress:1;
 79 };

–64–>kobject对象的名字
–65–>kobject对象之间的连接件
–66–>该kobject对象的父kobject对象
–67–>该kobject所属的kset
–68–>该kobject附属的ktype
–69–>该kobject在sysfs中的形式,比如符号链接,目录以及属性等信息
–70–>引用计数成员,本质是一个原子变量,用于决定何时释放对象
–74–>1bit,如果该kobject对象已经被初始化,则为1
–75–>1bit,如果该kobject对象已经被添加到sysfs中,则为1
–76–>1bit,如果该kobject对象已经发送过uevent add事件到用户空间,则为1
–77–>1bit,如果该kobject对象已经发送过uevent remove事件到用户空间,则为1
–78–>1bit,如果该kobject对象”抑制”发送事件到用户空间,则为1

kobj_init()

//lib/kobject.c
 314 void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
 315 {
 316         char *err_str;
            ...
 333         kobject_init_internal(kobj);
 334         kobj->ktype = ktype;
 335         return;
 340 }   

//lib/kobject.c
 187 static void kobject_init_internal(struct kobject *kobj) 
 188 {
 189         if (!kobj)
 190                 return;
 191         kref_init(&kobj->kref);
 192         INIT_LIST_HEAD(&kobj->entry);
 193         kobj->state_in_sysfs = 0;
 194         kobj->state_add_uevent_sent = 0;
 195         kobj->state_remove_uevent_sent = 0;
 196         kobj->state_initialized = 1;
 197 }

–334–>初始化ktype成员
–191–>初始化引用计数
–192–>初始化list_head连接件
–193-196–>初始化kobject状态

kobject_add()

添加kobject实例到内核数据结构的接口

//lib/kobject.c
 382 int kobject_add(struct kobject *kobj, struct kobject *parent,
 383                 const char *fmt, ...)
 384 {
 385         va_list args;
 386         int retval;
            ...
 398         va_start(args, fmt);
 399         retval = kobject_add_varg(kobj, parent, fmt, args);
 400         va_end(args);
 401 
 402         return retval;
 403 }
 
 343 static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
 344                             const char *fmt, va_list vargs)
 345 {
 346         int retval;
 348         retval = kobject_set_name_vargs(kobj, fmt, vargs);
            ...
 353         kobj->parent = parent;
 354         return kobject_add_internal(kobj);
 355 }       
 
 200 static int kobject_add_internal(struct kobject *kobj)
 201 {
 202         int error = 0;   
 203         struct kobject *parent;
 213 
 214         parent = kobject_get(kobj->parent);
 215 
 216         /* join kset if set, use it as parent if we do not already have one */
 217         if (kobj->kset) {
 218                 if (!parent)
 219                         parent = kobject_get(&kobj->kset->kobj);
 220                 kobj_kset_join(kobj);
 221                 kobj->parent = parent;
 222         }
 228 
 229         error = create_dir(kobj);
 230         if (error) {
            ...
 245         } else
 246                 kobj->state_in_sysfs = 1;
 248         return error;
 249 }

–382–>给kobject命名的格式字符串
–348–>初始化kobject->name成员
–353–>初始化kobject->parent成员
–214–>将kobject->parent的引用计数,同时暂存kobject->parent
–219–>将kobject的parent设为kset->kobj
–220–>kobj->kset引用计数+1,将kobj通过kobj->entry链接到kobj->kset->list队列尾,入队用spinlock
–221–>初始化kobject->parent
–229–>在sysfs下创建相应目录
–246–>如果sysfs目录创建成功,设置kobject标志位

kobject_uevent()

当在一个kobject发生某些事件,希望上报uevent事件到用户空间的时候,就需要调用这个函数,
这个函数是对kobject_uevent_env()的再次封装。
uevent事件的本质将一组环境变量值封装到kobj_uevent_env并通过netlink或udevhelper来发送给用户空间
支持的事件如下:

//include/linux/kobject.h
 53 enum kobject_action {
 54         KOBJ_ADD,
 55         KOBJ_REMOVE,
 56         KOBJ_CHANGE,
 57         KOBJ_MOVE,
 58         KOBJ_ONLINE,
 59         KOBJ_OFFLINE,
 60         KOBJ_MAX
 61 };

–54–>一个kobject对象被添加到内核,比如hotpulg一个设备
–55–>一个kobject对象从内核被移除,比如hotunplug一个设备
–56–>一个kobject对象的属性发生了改变,eg, hd_part分区表发生了改变
–57–>一个kobject对象的位置发生了改变,或者设备名发生了改变
–58-59–>一个kobject对象在线/离线,比如一个CPU
–60–>不是一个事件,仅表示事件的个数

//include/linux/kobject.h
123 struct kobj_uevent_env {
124         char *argv[3]; 
125         char *envp[UEVENT_NUM_ENVP];
126         int envp_idx;
127         char buf[UEVENT_BUFFER_SIZE];
128         int buflen;
129 };

–124–>XXX
–125–>每一对KV的起始地址,UEVENT_NUM_ENVP==32,即一共最多32对KV
–126–>每增加一对KV,envp_idx++,所以这个域表示当前对象中的KV对个数
–127–>存储所有KV的buf,一共2048个字节,环境信息使用”KV键值对”的形式表示,所有的KV对都被封装在同一个buf中,使用”\0”分隔。


–128–>实际使用的buf长度

//lib/kobject_uevent.c
164 int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
165                        char *envp_ext[])
166 {
167         struct kobj_uevent_env *env;
168         const char *action_string = kobject_actions[action];
169         const char *devpath = NULL;
170         const char *subsystem;
171         struct kobject *top_kobj;
172         struct kset *kset;
173         const struct kset_uevent_ops *uevent_ops;
174         int i = 0;
175         int retval = 0;
176 #ifdef CONFIG_NET
177         struct uevent_sock *ue_sk;
178 #endif
179 
180         pr_debug("kobject: '%s' (%p): %s\n",
181                  kobject_name(kobj), kobj, __func__);
182 
183         /* search the kset we belong to */
184         top_kobj = kobj;
185         while (!top_kobj->kset && top_kobj->parent)
186                 top_kobj = top_kobj->parent;
187 
188         if (!top_kobj->kset) {
192                 return -EINVAL;
193         }
194 
195         kset = top_kobj->kset;
196         uevent_ops = kset->uevent_ops;
197 
198         /* skip the event, if uevent_suppress is set*/
199         if (kobj->uevent_suppress) {
203                 return 0;
204         }
205         /* skip the event, if the filter returns zero. */
206         if (uevent_ops && uevent_ops->filter)
207                 if (!uevent_ops->filter(kset, kobj)) {
211                         return 0;
212                 }
213 
214         /* originating subsystem */
215         if (uevent_ops && uevent_ops->name)
216                 subsystem = uevent_ops->name(kset, kobj);
217         else
218                 subsystem = kobject_name(&kset->kobj);
219         if (!subsystem) {
223                 return 0;
224         }
225 
226         /* environment buffer */
227         env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
230 
231         /* complete object path */
232         devpath = kobject_get_path(kobj, GFP_KERNEL);
237 
238         /* default keys */
239         retval = add_uevent_var(env, "ACTION=%s", action_string);
242         retval = add_uevent_var(env, "DEVPATH=%s", devpath);
245         retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
248 
249         /* keys passed in from the caller */
250         if (envp_ext) {
251                 for (i = 0; envp_ext[i]; i++) {
252                         retval = add_uevent_var(env, "%s", envp_ext[i]);
255                 }
256         }
257 
258         /* let the kset specific function add its stuff */
259         if (uevent_ops && uevent_ops->uevent) {
260                 retval = uevent_ops->uevent(kset, kobj, env);
267         }
275         if (action == KOBJ_ADD)
276                 kobj->state_add_uevent_sent = 1;
277         else if (action == KOBJ_REMOVE)
278                 kobj->state_remove_uevent_sent = 1;
279 
280         mutex_lock(&uevent_sock_mutex);
281         /* we will send an event, so request a new sequence number */
282         retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)++uevent_seqnum);
288 #if defined(CONFIG_NET)
289         /* send netlink message */
290         list_for_each_entry(ue_sk, &uevent_sock_list, list) {
291                 struct sock *uevent_sock = ue_sk->sk;
292                 struct sk_buff *skb;
293                 size_t len;
294 
295                 if (!netlink_has_listeners(uevent_sock, 1))
296                         continue;
297 
298                 /* allocate message with the maximum possible size */
299                 len = strlen(action_string) + strlen(devpath) + 2;
300                 skb = alloc_skb(len + env->buflen, GFP_KERNEL);
301                 if (skb) {
302                         char *scratch;
303 
304                         /* add header */
305                         scratch = skb_put(skb, len);
306                         sprintf(scratch, "%s@%s", action_string, devpath);
307 
308                         /* copy keys to our continuous event payload buffer */
309                         for (i = 0; i < env->envp_idx; i++) {
                                ...
313                         }
314 
315                         NETLINK_CB(skb).dst_group = 1;
316                         retval = netlink_broadcast_filtered(uevent_sock, skb,0, 1, GFP_KERNEL,kobj_bcast_filter,kobj);
322                                 retval = 0;
323                 } else
324                         retval = -ENOMEM;
325         }
326 #endif
327         mutex_unlock(&uevent_sock_mutex);
328                         
329 #ifdef CONFIG_UEVENT_HELPER
            ...
354 #endif
355 
356 exit:
359         return retval;
360 }

–185–>逐级向上,直到找到kobject对象的kset或已经没有parent了
–188–>对上一级的结果进行判断,如果最终是没有kset,返回错误
–195-196–>保存kset及其kset_uevent_ops
–198-226–>逐项检查是否具备发送uevent条件
–198–>如果uevent_suppress位被设置,则直接返回
–206–>如果所属的kset->kset_uevent_ops->filter()返回0,则直接返回
–215–>试图获取所属的kset的name作为subsystem的名字,获取失败则使用kobject本身的name
–216–>具备发送uevent条件,分配env buf
–232–>获取kobject路径,即在sysfs中的位置
–238-245–>env->buf中填入ACTION、DEVPATH、SUBSYSTEM的key及其value
–250–>调用者自定义的KV值
–259–>添加所属kset的相关信息
–275–>如果ACTION是ADD或REMOVE,要对kobject中的相应bit置位
–282–>填入SEQNUM的key及其value,这个值是事件序号
–288–>env在前文已经准备就绪,如果系统中使用netlink,就在这里发送uevent事件
–290–>遍历所有注册到系统中的netlink_socket
–295–>如果没有进程监视这个netlink_sock,continue
–300–>分配skbuf接收环境变量
–316–>广播这个skbuf,listener将会侦听到这个netlink_message
–329–>对于使用uevent_helper机制的内核,在这里向用户空间上报uevent
综上,有以下情况不会上报uevent事件:

  1. kobjetc->uevent_suppress为1
  2. kset->filter()返回0
  3. NULL==kset->name
  4. kset->name()返回NULL。

kset

在sysfs中,用户空间看到一个目录对应内核空间的一个kobject对象,一个文件对应kobject的一个关联attribute(ktype),kset是对kobject的封装。

//include/linux/kobject.h
167 struct kset {             
168         struct list_head list;  
169         spinlock_t list_lock;   
170         struct kobject kobj;    
171         const struct kset_uevent_ops *uevent_ops;
172 };

–168–>kset对象和它附属的所有kobject对象的连接件
–169–>操作附属kobject需要的锁
–170–>内嵌的kobject,附属的kobject的parent指向kset的kobject,通过kobject对象之间的parent-child关系组织成属性结构供sysfs使用(其实与kset无关)
–171–>针对该kset附属的所有的kobject,一旦有事件发生(eg,add or remove)都会调用这个接口,eg. 这个接口可以影响热插拔上报到用户空间的表现

ktype

kobj_type用来表征一个kobject对象的”类型”

//include/linux/sysfs.h
184 struct sysfs_ops {                                                                                  
185         ssize_t (*show)(struct kobject *, struct attribute *, char *);
186         ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
187 };
//include/linux/kobject.h
115 struct kobj_type {
116         void (*release)(struct kobject *kobj);
117         const struct sysfs_ops *sysfs_ops;
118         struct attribute **default_attrs;
119         const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
120         const void *(*namespace)(struct kobject *kobj);
121 };

–116–>附属到的kobject被释放时回调的方法
–117–>读写相应kobject时回调的方法,读->show,写->store
–118–>该kobj_type默认的属性,每一个属性都对应到用户空间sysfs下的一个文件,都可以有自己的show/store方法(也可以没有),存储在container_of(attribute, struct kobj_attribute,attr)的kobj_attribute中。通常一个ktype都不是一个属性,所以是二级指针,实际中使用的常常是结构体指针数组。

__ATTR()

构造kobj_type的核心是构造”kobj_attribute”。如前文所述,内核提供了一组API来供我们快速的构造kobj_attribute并与kobject(实质为kobject->ktype)绑定,这种绑定会直接体现在sysfs文件系统中, 关于syfs的构造, 将是另外一篇文章要讨论的内容

//include/linux/sysfs.h
 75 __ATTR(_name, _mode, _show, _store)
 82 __ATTR_PREALLOC(_name, _mode, _show, _store)
 89 __ATTR_RO(_name)
 94 __ATTR_WO(_name) 
 99 __ATTR_RW(_name)
105  __ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store)
115 __ATTRIBUTE_GROUPS(_name) 
121 ATTRIBUTE_GROUPS(_name)  
125 __ATTRIBUTE_GROUPS(_name)

–75–125–>这些宏用于快速构造attribute结构体

最后, 附一张全图:

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.