标签:
本文将对Linux系统中的sysfs进行简单的分析,要分析sysfs就必须分析内核的driver-model(驱动模型),两者是紧密联系的。在分析过程中,本文将以platform总线和spi主控制器的platform驱动为例来进行讲解。其实,platform机制是基于driver-model的,通过本文,也会对platform机制有个简单的了解。
内核版本:2.6.30
1. What is sysfs?
个人理解:sysfs向用户空间展示了驱动设备的层次结构。我们都知道设备和对应的驱动都是由内核管理的,这些对于用户空间是不可见的。现在通过sysfs,可以在用户空间直观的了解设备驱动的层次结构。
我们来看看sysfs的文件结构:
[root@yj423 /sys]#ls
block class devices fs module
bus dev firmware kernel power
block:块设备
bus:系统中的总线
class: 设备类型,比如输入设备
dev:系统中已注册的设备节点的视图,有两个子目录char和block。
devices:系统中所有设备拓扑结构视图
fireware:固件
fs:文件系统
kernel:内核配置选项和状态信息
module:模块
power:系统的电源管理数据
2. kobject ,kset和ktype
要分析sysfs,首先就要分析kobject和kset,因为驱动设备的层次结构的构成就是由这两个东东来完成的。
2.1 kobject
kobject是一个对象的抽象,它用于管理对象。每个kobject对应着sysfs中的一个目录。
kobject用struct kobject来描述。
- 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;
- unsigned int uevent_suppress:1;
- };
2.2 kset
kset是一些kobject的集合,这些kobject可以有相同的ktype,也可以不同。同时,kset自己也包含一个kobject。在sysfs中,kset也是对应这一个目录,但是目录下面包含着其他的kojbect。
kset使用struct kset来描述。
- struct kset {
- struct list_head list;
- spinlock_t list_lock;
- struct kobject kobj;
-
- struct kset_uevent_ops *uevent_ops;
- };
2.3 ktype
每个kobject对象都内嵌有一个ktype,该结构定义了kobject在创建和删除时所采取的行为。
- struct kobj_type {
- void (*release)(struct kobject *kobj);
- struct sysfs_ops *sysfs_ops;
- struct attribute **default_attrs;
- };
-
- struct sysfs_ops {
- ssize_t (*show)(struct kobject *, struct attribute *,char *);
- ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
- };
-
- struct attribute {
- const char *name;
- struct module *owner;
- mode_t mode;
- };
当kobject的引用计数为0时,通过release方法来释放相关的资源。
attribute为属性,每个属性在sysfs中都有对应的属性文件。
sysfs_op的两个方法用于实现读取和写入属性文件时应该采取的行为。
2.4 kobject与kset的关系
下面这张图非常经典。最下面的kobj都属于一个kset,同时这些kobj的父对象就是kset内嵌的kobj。通过链表,kset可以获取所有属于它的kobj。
从sysfs角度而言,kset代表一个文件夹,而下面的kobj就是这个文件夹里面的内容,而内容有可能是文件也有可能是文件夹。
3.举例
在上一节中,我们知道sys下有一个bus目录,这一将分析如何通过kobject创建bus目录。
下面代码位于drivers/base/bus.c
- int __init buses_init(void)
- {
- bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
- if (!bus_kset)
- return -ENOMEM;
- return 0;
- }
-
- static struct kset_uevent_ops bus_uevent_ops = {
- .filter = bus_uevent_filter,
- };
-
- static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
- {
- struct kobj_type *ktype = get_ktype(kobj);
-
- if (ktype == &bus_ktype)
- return 1;
- return 0;
- }
这里直接调用kset_create_and_add,第一个参数为要创建的目录的名字,而第三个参数表示没有父对象。
下面代码位于drivers/base/kobject.c
- struct kset *kset_create_and_add(const char *name,
- struct kset_uevent_ops *uevent_ops,
- struct kobject *parent_kobj)
- {
- struct kset *kset;
- int error;
-
- kset = kset_create(name, uevent_ops, parent_kobj);
- if (!kset)
- return NULL;
- error = kset_register(kset);
- if (error) {
- kfree(kset);
- return NULL;
- }
- return kset;
- }
这里主要调用了两个函数,接下分别来看下。
3.1 kset_create函数
下面代码位于drivers/base/kobject.c
- static struct kset *kset_create(const char *name,
- struct kset_uevent_ops *uevent_ops,
- struct kobject *parent_kobj)
- {
- struct kset *kset;
-
- kset = kzalloc(sizeof(*kset), GFP_KERNEL);
- if (!kset)
- return NULL;
- kobject_set_name(&kset->kobj, name);
- kset->uevent_ops = uevent_ops;
- kset->kobj.parent = parent_kobj;
-
-
- kset->kobj.ktype = &kset_ktype;
- kset->kobj.kset = NULL;
-
- return kset;
- }
这个函数中,动态分配了kset结构,调用kobject_set_name设置kset->kobj->name为bus,也就是我们要创建的目录bus。同时这里kset->kobj.parent为NULL,
也就是没有父对象。因为要创建的bus目录是在sysfs所在的根目录创建的,自然没有父对象。
随后简要看下由kobject_set_name函数调用引发的一系列调用。
- int kobject_set_name(struct kobject *kobj, const char *fmt, ...)
- {
- va_list vargs;
- int retval;
-
- va_start(vargs, fmt);
- retval = kobject_set_name_vargs(kobj, fmt, vargs);
- va_end(vargs);
-
- return retval;
- }
-
- int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
- va_list vargs)
- {
- const char *old_name = kobj->name;
- char *s;
-
- if (kobj->name && !fmt)
- return 0;
-
- kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
- if (!kobj->name)
- return -ENOMEM;
-
-
- while ((s = strchr(kobj->name, ‘/‘)))
- s[0] = ‘!‘;
-
- kfree(old_name);
- return 0;
- }
-
- char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)
- {
- unsigned int len;
- char *p;
- va_list aq;
-
- va_copy(aq, ap);
- len = vsnprintf(NULL, 0, fmt, aq);
- va_end(aq);
-
- p = kmalloc(len+1, gfp);
- if (!p)
- return NULL;
-
- vsnprintf(p, len+1, fmt, ap);
-
- return p;
- }
3.2 kset_register
下面代码位于drivers/base/kobject.c。
- int kset_register(struct kset *k)
- {
- int err;
-
- if (!k)
- return -EINVAL;
-
- kset_init(k);
- err = kobject_add_internal(&k->kobj);
- if (err)
- return err;
- kobject_uevent(&k->kobj, KOBJ_ADD);
- return 0;
- }
这里面调用了3个函数。这里先介绍前两个函数。
3.2.1 kset_init
该函数用于初始化kset。
下面代码位于drivers/base/kobject.c。
- void kset_init(struct kset *k)
- {
- kobject_init_internal(&k->kobj);
- INIT_LIST_HEAD(&k->list);
- spin_lock_init(&k->list_lock);
- }
-
- static void kobject_init_internal(struct kobject *kobj)
- {
- if (!kobj)
- return;
- kref_init(&kobj->kref);
- INIT_LIST_HEAD(&kobj->entry);
- kobj->state_in_sysfs = 0;
- kobj->state_add_uevent_sent = 0;
- kobj->state_remove_uevent_sent = 0;
- kobj->state_initialized = 1;
- }
3.2.2 kobject_add_internal
该函数将在sysfs中建立目录。
下面代码位于drivers/base/kobject.c。
- static int kobject_add_internal(struct kobject *kobj)
- {
- int error = 0;
- struct kobject *parent;
-
- if (!kobj)
- return -ENOENT;
-
- if (!kobj->name || !kobj->name[0]) {
- WARN(1, "kobject: (%p): attempted to be registered with empty "
- "name!\n", kobj);
- return -EINVAL;
- }
-
- parent = kobject_get(kobj->parent);
-
-
- if (kobj->kset) {
- if (!parent)
-
- parent = kobject_get(&kobj->kset->kobj);
- kobj_kset_join(kobj);
- kobj->parent = parent;
- }
-
- pr_debug("kobject: ‘%s‘ (%p): %s: parent: ‘%s‘, set: ‘%s‘\n",
- kobject_name(kobj), kobj, __func__,
- parent ? kobject_name(parent) : "<NULL>",
- kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
-
- error = create_dir(kobj);
- if (error) {
- kobj_kset_leave(kobj);
- kobject_put(parent);
- kobj->parent = NULL;
-
-
- if (error == -EEXIST)
- printk(KERN_ERR "%s failed for %s with "
- "-EEXIST, don‘t try to register things with "
- "the same name in the same directory.\n",
- __func__, kobject_name(kobj));
- else
- printk(KERN_ERR "%s failed for %s (%d)\n",
- __func__, kobject_name(kobj), error);
- dump_stack();
- } else
- kobj->state_in_sysfs = 1;
-
- return error;
- }
在上面的kset_create中有kset->kobj.kset = NULL,因此if (kobj->kset)条件不满足。因此在这个函数中,对name进行了必要的检查之后,调用了create_dir在sysfs中创建目录。
在create_dir执行完成以后会在sysfs的根目录(/sys/)建立文件夹bus。该函数的详细分析将在后面给出。
至此,对bus目录的建立有了简单而直观的了解。我们可以看出kset其实就是表示一个文件夹,而kset本身也含有一个kobject,而该kobject的name字段即为该目录的名字,本例中为bus。
4. driver model
第2节所介绍的是最底层,最核心的内容。下面开始将描述较为高层的内容。
Linux设备模型使用了三个数据结构分别来描述总线、设备和驱动。所有的设备和对应的驱动都必须挂载在某一个总线上,通过总线,可以绑定设备和驱动。
这个属于分离的思想,将设备和驱动分开管理。
同时驱动程序可以了解到所有它所支持的设备,同样的,设备也能知道它对应驱动程序。
4.1 bus
总线是处理器与一个设备或者多个设备之间的通道。在设备模型中,所有的设备都挂载在某一个总线上。总线使用struct bus_type来表述。
下列代码位于include/linux/device.h。
- <strong><span style="font-family:System;font-size:12px;">struct bus_type {
- const char *name;
- struct bus_attribute *bus_attrs;
- struct device_attribute *dev_attrs;
- struct driver_attribute *drv_attrs;
-
- int (*match)(struct device *dev, struct device_driver *drv);
- int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
- int (*probe)(struct device *dev);
- int (*remove)(struct device *dev);
- void (*shutdown)(struct device *dev);
-
- int (*suspend)(struct device *dev, pm_message_t state);
- int (*suspend_late)(struct device *dev, pm_message_t state);
- int (*resume_early)(struct device *dev);
- int (*resume)(struct device *dev);
-
- struct dev_pm_ops *pm;
-
- struct bus_type_private *p;
- };
-
- struct bus_type_private {
- struct kset subsys;
- struct kset *drivers_kset;
- struct kset *devices_kset;
- struct klist klist_devices;
- struct klist klist_drivers;
- struct blocking_notifier_head bus_notifier;
- unsigned int drivers_autoprobe:1;
- struct bus_type *bus;
- };</span></strong>
我们看到每个bus_type都包含一个kset对象subsys,该kset在/sys/bus/目录下有着对应的一个目录,目录名即为字段name。后面我们将看到platform总线的建立。
drivers_kset和devices_kset对应着两个目录,该两个目录下将包含该总线上的设备和相应的驱动程序。
同时总线上的设备和驱动将分别保存在两个链表中:klist_devices和klist_drivers。
4.2 device
设备对象在driver-model中使用struct device来表示。
下列代码位于include/linux/device.h。
- struct device {
- struct device *parent;
-
- struct device_private *p;
-
- struct kobject kobj;
- const char *init_name;
- struct device_type *type;
-
- struct semaphore sem;
-
- struct bus_type *bus;
- struct device_driver *driver;
- void *driver_data;
- void *platform_data;
- struct dev_pm_info power;
-
- #ifdef CONFIG_NUMA
- int numa_node;
- #endif
- u64 *dma_mask;
- u64 coherent_dma_mask;
-
- struct device_dma_parameters *dma_parms;
-
- struct list_head dma_pools;
-
- struct dma_coherent_mem *dma_mem;
-
- struct dev_archdata archdata;
-
- dev_t devt;
-
- spinlock_t devres_lock;
- struct list_head devres_head;
-
- struct klist_node knode_class;
- struct class *class;
- struct attribute_group **groups;
-
- void (*release)(struct device *dev);
- };
-
- struct device_private {
- struct klist klist_children;
- struct klist_node knode_parent;
- struct klist_node knode_driver;
- struct klist_node knode_bus;
- struct device *device;
- };
device本身包含一个kobject,也就是说这个device在sysfs的某个地方有着一个对应的目录。
该device所挂载的bus由knode_bus指定。
该device所对应的设备驱动由knode_driver指定。
4.3 driver
设备设备对象在driver-model中使用struct device_driver来表示。
下列代码位于include/linux/device.h。
- struct device_driver {
- const char *name;
- struct bus_type *bus;
-
- struct module *owner;
- const char *mod_name;
-
- int (*probe) (struct device *dev);
- int (*remove) (struct device *dev);
- void (*shutdown) (struct device *dev);
- int (*suspend) (struct device *dev, pm_message_t state);
- int (*resume) (struct device *dev);
- struct attribute_group **groups;
-
- struct dev_pm_ops *pm;
-
- struct driver_private *p;
- };
-
- struct driver_private {
- struct kobject kobj;
- struct klist klist_devices;
- struct klist_node knode_bus;
- struct module_kobject *mkobj;
- struct device_driver *driver;
- };
device_driver本身包含一个kobject,也就是说这个device_driver在sysfs的某个地方有着一个对应的目录。
该设备驱动所支持的设备由klist_devices指定。
该设备驱动所挂载的总线由knode_bus制定。
5. Bus举例
本节我们将以platform总线为例,来看看,/sys/bus/platform是如何建立的。
platform总线的注册是由platform_bus_init函数完成的。该函数在内核启动阶段被调用,我们来简单看下调用过程:
start_kernel() -> rest_init() ->kernel_init() -> do_basic_setup() -> driver_init() -> platform_bus_init()。
注:kernel_init()是在rest_init函数中创建内核线程来执行的。
- int __init platform_bus_init(void)
- {
- int error;
-
- early_platform_cleanup();
-
- error = device_register(&platform_bus);
- if (error)
- return error;
- error = bus_register(&platform_bus_type);
- if (error)
- device_unregister(&platform_bus);
- return error;
- }
- struct bus_type platform_bus_type = {
- .name = "platform",
- .dev_attrs = platform_dev_attrs,
- .match = platform_match,
- .uevent = platform_uevent,
- .pm = PLATFORM_PM_OPS_PTR,
- };
- EXPORT_SYMBOL_GPL(platform_bus_type);
从bus_type,我们看到该总线的名字为platform。
调用了两个函数,我们只关注bus_register函数。
- int bus_register(struct bus_type *bus)
- {
- int retval;
- struct bus_type_private *priv;
-
- priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->bus = bus;
- bus->p = priv;
-
- BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
-
- retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
- if (retval)
- goto out;
-
- priv->subsys.kobj.kset = bus_kset;
- priv->subsys.kobj.ktype = &bus_ktype;
- priv->drivers_autoprobe = 1;
-
-
- retval = kset_register(&priv->subsys);
- if (retval)
- goto out;
-
-
- retval = bus_create_file(bus, &bus_attr_uevent);
- if (retval)
- goto bus_uevent_fail;
-
-
- priv->devices_kset = kset_create_and_add("devices", NULL,
- &priv->subsys.kobj);
- if (!priv->devices_kset) {
- retval = -ENOMEM;
- goto bus_devices_fail;
- }
-
-
- priv->drivers_kset = kset_create_and_add("drivers", NULL,
- &priv->subsys.kobj);
- if (!priv->drivers_kset) {
- retval = -ENOMEM;
- goto bus_drivers_fail;
- }
-
- klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
- klist_init(&priv->klist_drivers, NULL, NULL);
-
-
- retval = add_probe_files(bus);
- if (retval)
- goto bus_probe_files_fail;
-
- retval = bus_add_attrs(bus);
- if (retval)
- goto bus_attrs_fail;
-
- pr_debug("bus: ‘%s‘: registered\n", bus->name);
- return 0;
-
- bus_attrs_fail:
- remove_probe_files(bus);
- bus_probe_files_fail:
- kset_unregister(bus->p->drivers_kset);
- bus_drivers_fail:
- kset_unregister(bus->p->devices_kset);
- bus_devices_fail:
- bus_remove_file(bus, &bus_attr_uevent);
- bus_uevent_fail:
- kset_unregister(&bus->p->subsys);
- kfree(bus->p);
- out:
- bus->p = NULL;
- return retval;
- }
- EXPORT_SYMBOL_GPL(bus_register);
函数中,首先调用kobject_set_name设置了bus对象的subsys.kobject->name 为 platform,也就是说会建立一个名为platform的目录。kobject_set_name函数在3.1小节中已经给出。
在这里还用到了bus_kset这个变量,这个变量就是在第3节buses_init函数中建立bus目录所对应的kset对象。
接着,priv->subsys.kobj.kset = bus_kset,设置subsys的kobj在bus_kset对象包含的集合中,也就是说bus目录下将包含subsys对象所对应的目录,即platform。
紧接着调用了kset_register,参数为&priv->subsys。该函数在3.2节中以给出。在该函数的调用过程中,将调用kobj_kset_join函数,该函数将kobject添加到kobject->kset的链表中。
- static void kobj_kset_join(struct kobject *kobj)
- {
- if (!kobj->kset)
- return;
-
- kset_get(kobj->kset);
- spin_lock(&kobj->kset->list_lock);
- list_add_tail(&kobj->entry, &kobj->kset->list);
- spin_unlock(&kobj->kset->list_lock);
- }
kset_register函数执行完成后,将在/sys/bus/下建立目录platform。此刻,我们先来看下kset和kobject之间的关系。
然后,调用了bus_create_file函数在/sys/bus/platform/下建立文件uevent。
- int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
- {
- int error;
- if (bus_get(bus)) {
- error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
- bus_put(bus);
- } else
- error = -EINVAL;
- return error;
- }
- EXPORT_SYMBOL_GPL(bus_create_file);
有关底层的sysfs将在后面叙述,这里只要关注参数&bus->p->subsys.kobj,表示在该kset下建立文件,也就是platform下建立。
接着调用了2次kset_create_and_add,分别在/sys/bus/platform/下建立了文件夹devices和drivers。该函数位于第3节开始处。
这里和第3节调用kset_create_and_add时的最主要一个区别就是:此时的parent参数不为NULL,而是&priv->subsys.kobj。
也就是说,将要创建的kset的kobject->parent = &priv->subsys.kobj,也即新建的kset被包含在platform文件夹对应的kset中。
我们来看下关系图:
随后,调用了add_probe_files创建了属性文件drivers_autoprobe和drivers_probe。
- static int add_probe_files(struct bus_type *bus)
- {
- int retval;
-
- retval = bus_create_file(bus, &bus_attr_drivers_probe);
- if (retval)
- goto out;
-
- retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
- if (retval)
- bus_remove_file(bus, &bus_attr_drivers_probe);
- out:
- return retval;
- }
该函数只是简单的调用了两次bus_create_file,该函数已在前面叙述过。
最后调用bus_add_attrs创建总线相关的属性文件。
-
- static int bus_add_attrs(struct bus_type *bus)
- {
- int error = 0;
- int i;
-
- if (bus->bus_attrs) {
- for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
- error = bus_create_file(bus, &bus->bus_attrs[i]);
- if (error)
- goto err;
- }
- }
- done:
- return error;
- err:
- while (--i >= 0)
- bus_remove_file(bus, &bus->bus_attrs[i]);
- goto done;
- }
我们可以看到这个函数将根据bus_type->bus_arrts来创建属性文件。不过,在本例中,bus_arrts从未给出定义,因此次函数不做任何工作。
好了,整个bus_register调用完成了,我们来看下sysfs中实际的情况。
[root@yj423 platform]#pwd
/sys/bus/platform
[root@yj423 platform]#ls
devices drivers drivers_autoprobe drivers_probe uevent
最后,我们对整个bus_register的过程进行一个小结。
6. device举例
本节将首先讲述如何在/sys/devices下建立虚拟的platform设备,然后再讲述如何在/sys/devices/platform/下建立子设备。
6.1 虚拟的platform设备
之所以叫虚拟是因为这个platform并不代表任何实际存在的设备,但是platform将是所有具体设备的父设备。
在第5节,platform_bus_init函数中还调用了device_register,现在对其做出分析。
- int __init platform_bus_init(void)
- {
- int error;
-
- early_platform_cleanup();
-
- error = device_register(&platform_bus);
- if (error)
- return error;
- error = bus_register(&platform_bus_type);
- if (error)
- device_unregister(&platform_bus);
- return error;
- }
-
- struct device platform_bus = {
- .init_name = "platform",
- };
- EXPORT_SYMBOL_GPL(platform_bus)
下列函数位于drivers/base/core.c。
- int device_register(struct device *dev)
- {
- device_initialize(dev);
- return device_add(dev);
- }
一个设备的注册分成两部,每步通过调用一个函数函数。首先先看第一步:
下列函数位于drivers/base/core.c。
- void device_initialize(struct device *dev)
- {
- dev->kobj.kset = devices_kset;
- kobject_init(&dev->kobj, &device_ktype);
- INIT_LIST_HEAD(&dev->dma_pools);
- init_MUTEX(&dev->sem);
- spin_lock_init(&dev->devres_lock);
- INIT_LIST_HEAD(&dev->devres_head);
- device_init_wakeup(dev, 0);
- device_pm_init(dev);
- set_dev_node(dev, -1);
- }
6.1.1 有关devices_kset
首先其中用到了devices_kset对象,这个对象和第3节当中的bus_kset是同样的性质,也就是说该对象表示一个目录。
该对象的建立是在devices_init函数中完成的。
- int __init devices_init(void)
- {
- devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
- if (!devices_kset)
- return -ENOMEM;
- dev_kobj = kobject_create_and_add("dev", NULL);
- if (!dev_kobj)
- goto dev_kobj_err;
- sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
- if (!sysfs_dev_block_kobj)
- goto block_kobj_err;
- sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
- if (!sysfs_dev_char_kobj)
- goto char_kobj_err;
-
- return 0;
-
- char_kobj_err:
- kobject_put(sysfs_dev_block_kobj);
- block_kobj_err:
- kobject_put(dev_kobj);
- dev_kobj_err:
- kset_unregister(devices_kset);
- return -ENOMEM;
- }
由此可见,devices_kset对象表示的目录为/sys下的devices目录。
6.1.2 kobject_init
下列函数位于lib/kojbect.c。
- void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
- {
- char *err_str;
-
- if (!kobj) {
- err_str = "invalid kobject pointer!";
- goto error;
- }
- if (!ktype) {
- err_str = "must have a ktype to be initialized properly!\n";
- goto error;
- }
- if (kobj->state_initialized) {
-
- printk(KERN_ERR "kobject (%p): tried to init an initialized "
- "object, something is seriously wrong.\n", kobj);
- dump_stack();
- }
-
- kobject_init_internal(kobj);
- kobj->ktype = ktype;
- return;
-
- error:
- printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
- dump_stack();
- }
- EXPORT_SYMBOL(kobject_init);
-
- static void kobject_init_internal(struct kobject *kobj)
- {
- if (!kobj)
- return;
- kref_init(&kobj->kref);
- INIT_LIST_HEAD(&kobj->entry);
- kobj->state_in_sysfs = 0;
- kobj->state_add_uevent_sent = 0;
- kobj->state_remove_uevent_sent = 0;
- kobj->state_initialized = 1;
- }
该函数在做了一系列的必要检查后,调用kobject_init_internal初始化了kobject的某些字段。
6.1.3 device_init_wakeup
参数val为0,设置该device不能够唤醒。
- #ifdef CONFIG_PM
-
- static inline void device_init_wakeup(struct device *dev, int val)
- {
- dev->power.can_wakeup = dev->power.should_wakeup = !!val;
- }
- 。。。。。。
- #else /* !CONFIG_PM */
-
- static inline void device_init_wakeup(struct device *dev, int val)
- {
- dev->power.can_wakeup = !!val;
- }
- 。。。。。。
- #endif
6.1.4 device_pm_init
设置电源的状态。
- static inline void device_pm_init(struct device *dev)
- {
- dev->power.status = DPM_ON;
- }
6.1.5 set_dev_node
如果使用NUMA,则设置NUMA节点。
- #ifdef CONFIG_NUMA
- 。。。。。。
- static inline void set_dev_node(struct device *dev, int node)
- {
- dev->numa_node = node;
- }
- #else
- 。。。。。。
- static inline void set_dev_node(struct device *dev, int node)
- {
- }
- #endif
6.2 device_add
接下来是注册的第二步:调用device_add。
- int device_add(struct device *dev)
- {
- struct device *parent = NULL;
- struct class_interface *class_intf;
- int error = -EINVAL;
-
- dev = get_device(dev);
- if (!dev)
- goto done;
-
- dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);
- if (!dev->p) {
- error = -ENOMEM;
- goto done;
- }
- dev->p->device = dev;
- klist_init(&dev->p->klist_children, klist_children_get,
- klist_children_put);
-
-
- if (dev->init_name) {
- dev_set_name(dev, dev->init_name);
- dev->init_name = NULL;
- }
-
- if (!dev_name(dev))
- goto name_error;
-
- pr_debug("device: ‘%s‘: %s\n", dev_name(dev), __func__);
-
- parent = get_device(dev->parent);
- setup_parent(dev, parent);
-
-
- if (parent)
- set_dev_node(dev, dev_to_node(parent));
-
-
-
-
- error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
- if (error)
- goto Error;
-
-
- if (platform_notify)
- platform_notify(dev);
-
-
- error = device_create_file(dev, &uevent_attr);
- if (error)
- goto attrError;
-
- if (MAJOR(dev->devt)) {
- error = device_create_file(dev, &devt_attr);
- if (error)
- goto ueventattrError;
-
-
- error = device_create_sys_dev_entry(dev);
- if (error)
- goto devtattrError;
- }
-
- error = device_add_class_symlinks(dev);
- if (error)
- goto SymlinkError;
- error = device_add_attrs(dev);
- if (error)
- goto AttrsError;
- error = bus_add_device(dev);
- if (error)
- goto BusError;
- error = dpm_sysfs_add(dev);
- if (error)
- goto DPMError;
- device_pm_add(dev);
-
-
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_ADD_DEVICE, dev);
-
- kobject_uevent(&dev->kobj, KOBJ_ADD);
- bus_attach_device(dev);
- if (parent)
- klist_add_tail(&dev->p->knode_parent,
- &parent->p->klist_children);
-
- if (dev->class) {
- mutex_lock(&dev->class->p->class_mutex);
-
- klist_add_tail(&dev->knode_class,
- &dev->class->p->class_devices);
-
-
- list_for_each_entry(class_intf,
- &dev->class->p->class_interfaces, node)
- if (class_intf->add_dev)
- class_intf->add_dev(dev, class_intf);
- mutex_unlock(&dev->class->p->class_mutex);
- }
- done:
- put_device(dev);
- return error;
- DPMError:
- bus_remove_device(dev);
- BusError:
- device_remove_attrs(dev);
- AttrsError:
- device_remove_class_symlinks(dev);
- SymlinkError:
- if (MAJOR(dev->devt))
- device_remove_sys_dev_entry(dev);
- devtattrError:
- if (MAJOR(dev->devt))
- device_remove_file(dev, &devt_attr);
- ueventattrError:
- device_remove_file(dev, &uevent_attr);
- attrError:
- kobject_uevent(&dev->kobj, KOBJ_REMOVE);
- kobject_del(&dev->kobj);
- Error:
- cleanup_device_parent(dev);
- if (parent)
- put_device(parent);
- name_error:
- kfree(dev->p);
- dev->p = NULL;
- goto done;
- }
该函数调用了非常多的其他函数,接下来对主要的函数做出分析。
6.2.1 setup_parent函数
下列代码位于drivers/base/core.c。
- static void setup_parent(struct device *dev, struct device *parent)
- {
- struct kobject *kobj;
- kobj = get_device_parent(dev, parent);
- if (kobj)
- dev->kobj.parent = kobj;
- }
-
- static struct kobject *get_device_parent(struct device *dev,
- struct device *parent)
- {
-
- if (dev->class && (!parent || parent->class != dev->class))
- return &dev->class->p->class_subsys.kobj;
-
- else if (parent)
- return &parent->kobj;
-
- return NULL;
- }
该函数将设置dev对象的parent。在这里实际传入的parent为NULL,同时dev->class也没有定义过。因此这个函数什么都没有做。
6.2.2 kobject_add函数
下列代码位于lib/kobject.c。
- int kobject_add(struct kobject *kobj, struct kobject *parent,
- const char *fmt, ...)
- {
- va_list args;
- int retval;
-
- if (!kobj)
- return -EINVAL;
-
- if (!kobj->state_initialized) {
- printk(KERN_ERR "kobject ‘%s‘ (%p): tried to add an "
- "uninitialized object, something is seriously wrong.\n",
- kobject_name(kobj), kobj);
- dump_stack();
- return -EINVAL;
- }
- va_start(args, fmt);
- retval = kobject_add_varg(kobj, parent, fmt, args);
- va_end(args);
-
- return retval;
- }
- EXPORT_SYMBOL(kobject_add);
-
- static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
- const char *fmt, va_list vargs)
- {
- int retval;
-
- retval = kobject_set_name_vargs(kobj, fmt, vargs);
- if (retval) {
- printk(KERN_ERR "kobject: can not set name properly!\n");
- return retval;
- }
- kobj->parent = parent;
- return kobject_add_internal(kobj);
- }
-
- static int kobject_add_internal(struct kobject *kobj)
- {
- int error = 0;
- struct kobject *parent;
-
- if (!kobj)
- return -ENOENT;
-
- if (!kobj->name || !kobj->name[0]) {
- WARN(1, "kobject: (%p): attempted to be registered with empty "
- "name!\n", kobj);
- return -EINVAL;
- }
-
- parent = kobject_get(kobj->parent);
-
-
- if (kobj->kset) {
- if (!parent)
-
- parent = kobject_get(&kobj->kset->kobj);
- kobj_kset_join(kobj);
- kobj->parent = parent;
- }
-
- pr_debug("kobject: ‘%s‘ (%p): %s: parent: ‘%s‘, set: ‘%s‘\n",
- kobject_name(kobj), kobj, __func__,
- parent ? kobject_name(parent) : "<NULL>",
- kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
-
- error = create_dir(kobj);
- if (error) {
- kobj_kset_leave(kobj);
- kobject_put(parent);
- kobj->parent = NULL;
-
-
- if (error == -EEXIST)
- printk(KERN_ERR "%s failed for %s with "
- "-EEXIST, don‘t try to register things with "
- "the same name in the same directory.\n",
- __func__, kobject_name(kobj));
- else
- printk(KERN_ERR "%s failed for %s (%d)\n",
- __func__, kobject_name(kobj), error);
- dump_stack();
- } else
- kobj->state_in_sysfs = 1;
-
- return error;
- }
在调用时,参数parent为NULL,且dev->kobj.kset在6.1节device_initialize函数中设置为devices_kset。
而devices_kset对应着/sys/devices目录,因此该函数调用完成后将在/sys/devices目录下生成目录platform。
但是这里比较奇怪的是,为什么platform目录没有对应的kset对象???
6.2.3 device_create_sys_dev_entry函数
在调用该函数之前,会在/sys/devices/platform/下生成属性文件。接着如果该device的设备号不为0,则创建属性文件dev,并调用本函数。
但是,在本例中设备号devt从未设置过,显然为0,那么本函数实际并未执行。
下列代码位于drivers/base/core.c。
- static int device_create_sys_dev_entry(struct device *dev)
- {
- struct kobject *kobj = device_to_dev_kobj(dev);
- int error = 0;
- char devt_str[15];
-
- if (kobj) {
- format_dev_t(devt_str, dev->devt);
- error = sysfs_create_link(kobj, &dev->kobj, devt_str);
- }
-
- return error;
- }
- static struct kobject *device_to_dev_kobj(struct device *dev)
- {
- struct kobject *kobj;
-
- if (dev->class)
- kobj = dev->class->dev_kobj;
- else
- kobj = sysfs_dev_char_kobj;
-
- return kobj;
- }
6.2.4 device_add_class_symlinks函数
由于dev->class为NULL,本函数其实没做任何工作。
下列代码位于drivers/base/core.c。
- static int device_add_class_symlinks(struct device *dev)
- {
- int error;
-
- if (!dev->class)
- return 0;
-
- error = sysfs_create_link(&dev->kobj,
- &dev->class->p->class_subsys.kobj,
- "subsystem");
- if (error)
- goto out;
-
- #ifdef CONFIG_SYSFS_DEPRECATED
-
- if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
- device_is_not_partition(dev)) {
- error = sysfs_create_link(&dev->class->p->class_subsys.kobj,
- &dev->kobj, dev_name(dev));
- if (error)
- goto out_subsys;
- }
-
- if (dev->parent && device_is_not_partition(dev)) {
- struct device *parent = dev->parent;
- char *class_name;
-
-
- while (parent->class && !parent->bus && parent->parent)
- parent = parent->parent;
-
- error = sysfs_create_link(&dev->kobj,
- &parent->kobj,
- "device");
- if (error)
- goto out_busid;
-
- class_name = make_class_name(dev->class->name,
- &dev->kobj);
- if (class_name)
- error = sysfs_create_link(&dev->parent->kobj,
- &dev->kobj, class_name);
- kfree(class_name);
- if (error)
- goto out_device;
- }
- return 0;
-
- out_device:
- if (dev->parent && device_is_not_partition(dev))
- sysfs_remove_link(&dev->kobj, "device");
- out_busid:
- if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
- device_is_not_partition(dev))
- sysfs_remove_link(&dev->class->p->class_subsys.kobj,
- dev_name(dev));
- #else
-
- error = sysfs_create_link(&dev->class->p->class_subsys.kobj,
- &dev->kobj, dev_name(dev));
- if (error)
- goto out_subsys;
-
- if (dev->parent && device_is_not_partition(dev)) {
- error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,
- "device");
- if (error)
- goto out_busid;
- }
- return 0;
-
- out_busid:
- sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev_name(dev));
- #endif
-
- out_subsys:
- sysfs_remove_link(&dev->kobj, "subsystem");
- out:
- return error;
- }
6.2.5 device_add_attrs函数
同样dev->class为空,什么都没干。
下列代码位于drivers/base/core.c。
- static int device_add_attrs(struct device *dev)
- {
- struct class *class = dev->class;
- struct device_type *type = dev->type;
- int error;
-
- if (class) {
- error = device_add_attributes(dev, class->dev_attrs);
- if (error)
- return error;
- }
-
- if (type) {
- error = device_add_groups(dev, type->groups);
- if (error)
- goto err_remove_class_attrs;
- }
-
- error = device_add_groups(dev, dev->groups);
- if (error)
- goto err_remove_type_groups;
-
- return 0;
-
- err_remove_type_groups:
- if (type)
- device_remove_groups(dev, type->groups);
- err_remove_class_attrs:
- if (class)
- device_remove_attributes(dev, class->dev_attrs);
-
- return error;
- }
6.2.6 bus_add_device函数
由于dev->bus未指定,因此这个函数什么都没干。
该函数将创建三个symlink,在sysfs中建立总线和设备间的关系。
下列代码位于drivers/base/bus.c。
- int bus_add_device(struct device *dev)
- {
- struct bus_type *bus = bus_get(dev->bus);
- int error = 0;
-
- if (bus) {
- pr_debug("bus: ‘%s‘: add device %s\n", bus->name, dev_name(dev));
- error = device_add_attrs(bus, dev);
- if (error)
- goto out_put;
-
-
- error = sysfs_create_link(&bus->p->devices_kset->kobj,
- &dev->kobj, dev_name(dev));
- if (error)
- goto out_id;
-
-
- error = sysfs_create_link(&dev->kobj,
- &dev->bus->p->subsys.kobj, "subsystem");
- if (error)
- goto out_subsys;
-
-
- error = make_deprecated_bus_links(dev);
- if (error)
- goto out_deprecated;
- }
- return 0;
-
- out_deprecated:
- sysfs_remove_link(&dev->kobj, "subsystem");
- out_subsys:
- sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
- out_id:
- device_remove_attrs(bus, dev);
- out_put:
- bus_put(dev->bus);
- return error;
- }
6.2.7 dpm_sysfs_add函数
下列代码位于drivers/base/power/sysfs.c。
- int dpm_sysfs_add(struct device * dev)
- {
- return sysfs_create_group(&dev->kobj, &pm_attr_group);
- }
-
- static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
-
-
- static struct attribute * power_attrs[] = {
- &dev_attr_wakeup.attr,
- NULL,
- };
- static struct attribute_group pm_attr_group = {
- .name = "power",
- .attrs = power_attrs,
- };
该函数将在XXX目录下建立power子目录,并在该子目录下建立属性文件wakeup。
在本例中,将在/sys/bus/platform下建立子目录power并在子目录下建立wakeup文件。
6.2.8 device_pm_add函数
下列代码位于drivers/base/power/main.c。
- void device_pm_add(struct device *dev)
- {
- pr_debug("PM: Adding info for %s:%s\n",
- dev->bus ? dev->bus->name : "No Bus",
- kobject_name(&dev->kobj));
- mutex_lock(&dpm_list_mtx);
- if (dev->parent) {
- if (dev->parent->power.status >= DPM_SUSPENDING)
- dev_warn(dev, "parent %s should not be sleeping\n",
- dev_name(dev->parent));
- } else if (transition_started) {
-
- dev_WARN(dev, "Parentless device registered during a PM transaction\n");
- }
-
- list_add_tail(&dev->power.entry, &dpm_list);
- mutex_unlock(&dpm_list_mtx);
- }
该函数只是将设备添加到电源管理链表中。
6.2.9 bus_attach_device函数
在本例中,由于bus未指定,该函数实际不做任何工作。
下列代码位于drivers/base/bus.c。
- void bus_attach_device(struct device *dev)
- {
- struct bus_type *bus = dev->bus;
- int ret = 0;
-
- if (bus) {
- if (bus->p->drivers_autoprobe)
- ret = device_attach(dev);
- WARN_ON(ret < 0);
- if (ret >= 0)
- klist_add_tail(&dev->p->knode_bus,
- &bus->p->klist_devices);
- }
- }
-
- int device_attach(struct device *dev)
- {
- int ret = 0;
-
- down(&dev->sem);
- if (dev->driver) {
- ret = device_bind_driver(dev);
- if (ret == 0)
- ret = 1;
- else {
- dev->driver = NULL;
- ret = 0;
- }
- } else {
- ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
- }
- up(&dev->sem);
- return ret;
- }
- EXPORT_SYMBOL_GPL(device_attach);
如果bus存在的话,将会调用device_attach函数进行绑定工作。该函数首先判断dev->driver,如果非0,表示该设备已经绑定了驱动,只要在sysfs中建立链接关系即可。
为0表示没有绑定,接着调用bus_for_each_drv,注意作为参数传入的__device_attach,这是个函数,后面会调用它。
我们来看下bus_for_each_drv:
- int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
- void *data, int (*fn)(struct device_driver *, void *))
- {
- struct klist_iter i;
- struct device_driver *drv;
- int error = 0;
-
- if (!bus)
- return -EINVAL;
-
- klist_iter_init_node(&bus->p->klist_drivers, &i,
- start ? &start->p->knode_bus : NULL);
- while ((drv = next_driver(&i)) && !error)
- error = fn(drv, data);
- klist_iter_exit(&i);
- return error;
- }
- EXPORT_SYMBOL_GPL(bus_for_each_drv);
该函数将遍历总线的drivers目录下的所有驱动,也就是/sys/bus/XXX/drivers/下的目录,为该driver调用fn函数,也就是__device_attach。我们来看下:
- static int __device_attach(struct device_driver *drv, void *data)
- {
- struct device *dev = data;
-
- if (!driver_match_device(drv, dev))
- return 0;
-
- return driver_probe_device(drv, dev);
- }
-
- static inline int driver_match_device(struct device_driver *drv,
- struct device *dev)
- {
- return drv->bus->match ? drv->bus->match(dev, drv) : 1;
- }
-
- int driver_probe_device(struct device_driver *drv, struct device *dev)
- {
- int ret = 0;
-
- if (!device_is_registered(dev))
- return -ENODEV;
-
- pr_debug("bus: ‘%s‘: %s: matched device %s with driver %s\n",
- drv->bus->name, __func__, dev_name(dev), drv->name);
-
- ret = really_probe(dev, drv);
-
- return ret;
- }
该函数首先调用driver_match_device函数,后者将会调用总线的match方法,如果有的话,来进行匹配工作。如果没有该方法,则返回1,表示匹配成功。
我们这里是针对platform总线,该总线的方法将在7.6.2节中看到。
随后,又调用了driver_probe_device函数。该函数将首先判断该device是否已在sysfs中,如果在则调用really_probe,否则返回出错。
really_probe将会调用驱动的probe并完成绑定的工作。该函数将在7.6.2节中分析。
6.2.10 小结
在本例中,当device_register调用完成以后,将在/sys/devices/下建立目录platform,并在platfrom下建立属性文件uevent和子目录power,最后在power子目录下建立wakeup属性文件。
最后以函数调用过程的总结来结束第6.2小结。
6.3 spi主控制器的平台设备
本节对一个特定的platform设备进行讲解,那就是spi主控制器的平台设备。
在内核的启动阶段,platform设备将被注册进内核。我们来看下。
下列代码位于arch/arm/mach-s3c2440/mach-smdk2440.c
- static struct resource s3c_spi0_resource[] = {
- [0] = {
- .start = S3C24XX_PA_SPI,
- .end = S3C24XX_PA_SPI + 0x1f,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_SPI0,
- .end = IRQ_SPI0,
- .flags = IORESOURCE_IRQ,
- }
-
- };
-
- static u64 s3c_device_spi0_dmamask = 0xffffffffUL;
-
- struct platform_device s3c_device_spi0 = {
- .name = "s3c2410-spi",
- .id = 0,
- .num_resources = ARRAY_SIZE(s3c_spi0_resource),
- .resource = s3c_spi0_resource,
- .dev = {
- .dma_mask = &s3c_device_spi0_dmamask,
- .coherent_dma_mask = 0xffffffffUL
- }
- };
-
- static struct platform_device *smdk2440_devices[] __initdata = {
- &s3c_device_usb,
- &s3c_device_lcd,
- &s3c_device_wdt,
- &s3c_device_i2c0,
- &s3c_device_iis,
- &s3c_device_spi0,
- };
-
-
-
- static void __init smdk2440_machine_init(void)
- {
- s3c24xx_fb_set_platdata(&smdk2440_fb_info);
- s3c_i2c0_set_platdata(NULL);
-
- platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
- smdk_machine_init();
- }
在smdk2440_machine_init函数中,通过调用platform_add_devices将设备注册到内核中。接着来看下该函数。
6.3.1 platform_add_devices
- int platform_add_devices(struct platform_device **devs, int num)
- {
- int i, ret = 0;
-
- for (i = 0; i < num; i++) {
- ret = platform_device_register(devs[i]);
- if (ret) {
- while (--i >= 0)
- platform_device_unregister(devs[i]);
- break;
- }
- }
-
- return ret;
- }
- EXPORT_SYMBOL_GPL(platform_add_devices);
该函数将根据devs指针数组,调用platform_device_register将platform设备逐一注册进内核。
6.3.2 platform_device_register
- int platform_device_register(struct platform_device *pdev)
- {
- device_initialize(&pdev->dev);
- return platform_device_add(pdev);
- }
- EXPORT_SYMBOL_GPL(platform_device_register);
调用了两个函数,第一个函数在6.1节已经分析过。我们来看下第二个函数。
6.3.2 platform_device_register
- int platform_device_add(struct platform_device *pdev)
- {
- int i, ret = 0;
-
- if (!pdev)
- return -EINVAL;
-
- if (!pdev->dev.parent)
- pdev->dev.parent = &platform_bus;
-
- pdev->dev.bus = &platform_bus_type;
-
- if (pdev->id != -1)
- dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
- else
- dev_set_name(&pdev->dev, pdev->name);
-
-
- for (i = 0; i < pdev->num_resources; i++) {
- struct resource *p, *r = &pdev->resource[i];
-
- if (r->name == NULL)
- r->name = dev_name(&pdev->dev);
-
- p = r->parent;
- if (!p) {
- if (resource_type(r) == IORESOURCE_MEM)
- p = &iomem_resource;
- else if (resource_type(r) == IORESOURCE_IO)
- p = &ioport_resource;
- }
-
- if (p && insert_resource(p, r)) {
- printk(KERN_ERR
- "%s: failed to claim resource %d\n",
- dev_name(&pdev->dev), i);
- ret = -EBUSY;
- goto failed;
- }
- }
-
- pr_debug("Registering platform device ‘%s‘. Parent at %s\n",
- dev_name(&pdev->dev), dev_name(pdev->dev.parent));
-
- ret = device_add(&pdev->dev);
- if (ret == 0)
- return ret;
-
- failed:
- while (--i >= 0) {
- struct resource *r = &pdev->resource[i];
- unsigned long type = resource_type(r);
-
- if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
- release_resource(r);
- }
-
- return ret;
- }
- EXPORT_SYMBOL_GPL(platform_device_add);
在这个函数的最后赫然出现了device_add函数。我们回忆下在6.1节中device_register的注册过程,该函数只调用了两个函数,一个是device_initialize函数,另一个就是device_add。
本节的platform_device_register函数,首先也是调用了device_initialize,但是随后他做了一些其他的工作,最后调用了device_add。
那么这个"其他的工作"干了些什么呢?
首先,它将该SPI主控制对应的平台设备的父设备设为虚拟的platform设备(platform_bus),然后将该平台设备挂在至platform总线(platform_bus_type)上,这两步尤为重要,后面我们将看到。
然后,调用了dev_set_name设置了pdev->dev-kobj.name,也就是该设备对象的名字,这里的名字为s3c2410-spi.0,这个名字将被用来建立一个目录。
最后,将平台的相关资源添加到资源树中。这不是本篇文章讨论的重点所在,所以不做过多说明。
在"其他的工作""干完之后,调用了device_add函数。那么后面的函数调用过程将和6.2小结的一致。
由于“其他的工作”的原因,实际执行的过程和结果将有所区别。我们来分析下。
6.3.3 不一样device_add调用结果
首先,在device_add被调用之前,有若干个非常重要的条件已经被设置了。如下:
pdev->dev->kobj.kset = devices_kset
pdev->dev-.parent = &platform_bus
pdev->dev.bus = &platform_bus_type
set_up函数执行时,由于参数parent为&platform_bus,因此最后将设置pdev->dev->kobj.parent = platform_bus.kobj。平台设备对象的父对象为虚拟的platform设备。
kobject_add函数执行时,由于参数parent的存在,将在parent对象所对应的目录下创建另一个目录。parent对象代表目录/sys/devices/下的platform,因此将在/sys/devices/platform下建立目录s3c2410-spi.0。
device_create_file建立属性文件uevent。
bus_add_device函数执行时,由于dev.bus 为&platform_bus_type,因此将建立三个symlink。
/sys/devices/platform/s3c2410-spi.0下建立链接subsystem和bus,他们指向/sys/bus/platform。
/sys/bus/platform/devices/下建立链接s3c2410-spi.0,指向/sys/devices/platform/s3c2410-spi.0。
dpm_sysfs_add函数在/sys/devices/platform/s3c2410-spi.0下建立子目录power,并在该子目录下建立属性文件wakeup。
执行到这里时,sysfs已将内核中新添加的SPI主控制器平台设备呈现出来了,我们来验证下。
[root@yj423 s3c2410-spi.0]#pwd
/sys/devices/platform/s3c2410-spi.0
[root@yj423 s3c2410-spi.0]#ll
lrwxrwxrwx 1 root root 0 Jan 1 00:29 bus -> ../../../bus/platform
lrwxrwxrwx 1 root root 0 Jan 1 00:29 driver -> ../../../bus/platform/drivers/s3c2410-spi
-r--r--r-- 1 root root 4096 Jan 1 00:29 modalias
drwxr-xr-x 2 root root 0 Jan 1 00:29 power
drwxr-xr-x 3 root root 0 Jan 1 00:00 spi0.0
drwxr-xr-x 3 root root 0 Jan 1 00:00 spi0.1
lrwxrwxrwx 1 root root 0 Jan 1 00:29 spi_master:spi0 -> ../../../class/spi_master/spi0
lrwxrwxrwx 1 root root 0 Jan 1 00:29 subsystem -> ../../../bus/platform
-rw-r--r-- 1 root root 4096 Jan 1 00:29 uevent
[root@yj423 devices]#pwd
/sys/bus/platform/devices
[root@yj423 devices]#ll s3c2410-spi.0
lrwxrwxrwx 1 root root 0 Jan 1 00:44 s3c2410-spi.0 -> ../../../devices/platform/s3c2410-spi.0
通过sysfs将设备驱动的模型层次呈现在用户空间以后,将更新内核的设备模型之间的关系,这是通过修改链表的指向来完成的。
bus_attach_device函数执行时,将设备添加到总线的设备链表中,同时也会尝试绑定驱动,不过会失败。
接着,由于dev->parent的存在,将SPI主控制器设备添加到父设备platform虚拟设备的儿子链表中。
7. driver举例
我们已经介绍过platform总线的注册,也讲述了SPI主控制器设备作为平台设备的注册过程,在本节,将描述SPI主控制器的platform驱动是如何注册的。
7.1 s3c24xx_spi_init
下列代码位于drivers/spi/spi_s3c24xx.c。
- MODULE_ALIAS("platform:s3c2410-spi");
- static struct platform_driver s3c24xx_spi_driver = {
- .remove = __exit_p(s3c24xx_spi_remove),
- .suspend = s3c24xx_spi_suspend,
- .resume = s3c24xx_spi_resume,
- .driver = {
- .name = "s3c2410-spi",
- .owner = THIS_MODULE,
- },
- };
-
- static int __init s3c24xx_spi_init(void)
- {
- return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);
- }
驱动注册通过调用platform_driver_probe来完成。
注意:driver.name字段使用来匹配设备的,该字段必须和6.3节一开始给出的pdev.name字段相同。
7.2 platform_driver_probe
下列代码位于drivers/base/platform.c。
- int __init_or_module platform_driver_probe(struct platform_driver *drv,
- int (*probe)(struct platform_device *))
- {
- int retval, code;
-
-
- drv->probe = probe;
- retval = code = platform_driver_register(drv);
-
-
- spin_lock(&platform_bus_type.p->klist_drivers.k_lock);
- drv->probe = NULL;
- if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))
- retval = -ENODEV;
- drv->driver.probe = platform_drv_probe_fail;
- spin_unlock(&platform_bus_type.p->klist_drivers.k_lock);
-
- if (code != retval)
- platform_driver_unregister(drv);
- return retval;
- }
- EXPORT_SYMBOL_GPL(platform_driver_probe);
这里的重点是platform_driver_register,由它来完成了platform驱动的注册。
7.3 platform_driver_register
- int platform_driver_register(struct platform_driver *drv)
- {
- drv->driver.bus = &platform_bus_type;
- if (drv->probe)
- drv->driver.probe = platform_drv_probe;
- if (drv->remove)
- drv->driver.remove = platform_drv_remove;
- if (drv->shutdown)
- drv->driver.shutdown = platform_drv_shutdown;
- if (drv->suspend)
- drv->driver.suspend = platform_drv_suspend;
- if (drv->resume)
- drv->driver.resume = platform_drv_resume;
- return driver_register(&drv->driver);
- }
- EXPORT_SYMBOL_GPL(platform_driver_register);
driver_register函数就是driver注册的核心函数。需要注意的是,在调用函数之前,将该驱动所挂载的总线设置为platform总线(platform_bus_type)。
7.4 driver_register
下列代码位于drivers/base/driver.c。
- int driver_register(struct device_driver *drv)
- {
- int ret;
- struct device_driver *other;
-
- BUG_ON(!drv->bus->p);
-
- if ((drv->bus->probe && drv->probe) ||
- (drv->bus->remove && drv->remove) ||
- (drv->bus->shutdown && drv->shutdown))
- printk(KERN_WARNING "Driver ‘%s‘ needs updating - please use "
- "bus_type methods\n", drv->name);
-
- other = driver_find(drv->name, drv->bus);
- if (other) {
- put_driver(other);
- printk(KERN_ERR "Error: Driver ‘%s‘ is already registered, "
- "aborting...\n", drv->name);
- return -EEXIST;
- }
-
- ret = bus_add_driver(drv);
- if (ret)
- return ret;
- ret = driver_add_groups(drv, drv->groups);
- if (ret)
- bus_remove_driver(drv);
- return ret;
- }
- EXPORT_SYMBOL_GPL(driver_register);
这里主要调用两个函数driver_find和bus_add_driver。前者将通过总线来搜索该驱动是否存在,后者将添加驱动到总线中。
接下来就分析这两个函数。
7.5 driver_find
下列代码位于drivers/base/driver.c。
- struct device_driver *driver_find(const char *name, struct bus_type *bus)
- {
- struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
- struct driver_private *priv;
-
- if (k) {
- priv = to_driver(k);
- return priv->driver;
- }
- return NULL;
- }
- EXPORT_SYMBOL_GPL(driver_find);
- struct kobject *kset_find_obj(struct kset *kset, const char *name)
- {
- struct kobject *k;
- struct kobject *ret = NULL;
-
- spin_lock(&kset->list_lock);
- list_for_each_entry(k, &kset->list, entry) {
- if (kobject_name(k) && !strcmp(kobject_name(k), name)) {
- ret = kobject_get(k);
- break;
- }
- }
- spin_unlock(&kset->list_lock);
- return ret;
- }
这里调用了kset_find_obj函数,传入的实参bus->p->drivers_kset,它对应的就是/sys/bus/platform/下的drivers目录,然后通过链表,它将搜索该目录下的所有文件,来寻找是否有名为s3c2410-spi的文件。还记得吗? kobject就是一个文件对象。如果没有找到将返回NULL,接着将调用bus_add_driver把驱动注册进内核。
7.6 bus_add_driver
下列代码位于drivers/base/bus.c
- int bus_add_driver(struct device_driver *drv)
- {
- struct bus_type *bus;
- struct driver_private *priv;
- int error = 0;
-
- bus = bus_get(drv->bus);
- if (!bus)
- return -EINVAL;
-
- pr_debug("bus: ‘%s‘: add driver %s\n", bus->name, drv->name);
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- error = -ENOMEM;
- goto out_put_bus;
- }
-
- klist_init(&priv->klist_devices, NULL, NULL);
-
- priv->driver = drv;
- drv->p = priv;
-
- priv->kobj.kset = bus->p->drivers_kset;
- error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
- "%s", drv->name);
- if (error)
- goto out_unregister;
-
- if (drv->bus->p->drivers_autoprobe) {
- error = driver_attach(drv);
- if (error)
- goto out_unregister;
- }
-
- klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
- module_add_driver(drv->owner, drv);
-
-
- error = driver_create_file(drv, &driver_attr_uevent);
- if (error) {
- printk(KERN_ERR "%s: uevent attr (%s) failed\n",
- __func__, drv->name);
- }
-
- error = driver_add_attrs(bus, drv);
- if (error) {
-
- printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
- __func__, drv->name);
- }
-
- error = add_bind_files(drv);
- if (error) {
-
- printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
- __func__, drv->name);
- }
-
- kobject_uevent(&priv->kobj, KOBJ_ADD);
- return 0;
- out_unregister:
- kfree(drv->p);
- drv->p = NULL;
- kobject_put(&priv->kobj);
- out_put_bus:
- bus_put(bus);
- return error;
- }
在设置driver的kobj.kset为drivers目录所对应的kset之后,调用了kobject_init_and_add,我们来看下。
7.6.1 kobject_init_and_add
下列代码位于lib/kobject.c。
- int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
- struct kobject *parent, const char *fmt, ...)
- {
- va_list args;
- int retval;
-
- kobject_init(kobj, ktype);
-
- va_start(args, fmt);
- retval = kobject_add_varg(kobj, parent, fmt, args);
- va_end(args);
-
- return retval;
- }
- EXPORT_SYMBOL_GPL(kobject_init_and_add);
该函数中调用了两个函数,这两个函数分别在6.1.2和6.2.2中讲述过,这里不再赘述。
调用该函数时由于parent为NULL,但kobj.kset为drivers目录,所以将在/sys/bus/platform/drivers/下建立目录,名为s3c2410-spi。
我们来验证下:
[root@yj423 s3c2410-spi]#pwd
/sys/bus/platform/drivers/s3c2410-spi
接着由于drivers_autoprobe在bus_register执行的时候已经置1,将调用driver_attach。
7.6.2 driver_attach
下列代码位于drivers/base/dd.c。
- int driver_attach(struct device_driver *drv)
- {
- return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
- }
- EXPORT_SYMBOL_GPL(driver_attach);
该函数将调用bus_for_each_dev来寻找总线上的每个设备,这里的总线即为platform总线,然后尝试绑定设备。
这里需要注意的是最后一个参数__driver_attach,这是一个函数名,后面将会调用它。
- int bus_for_each_dev(struct bus_type *bus, struct device *start,
- void *data, int (*fn)(struct device *, void *))
- {
- struct klist_iter i;
- struct device *dev;
- int error = 0;
-
- if (!bus)
- return -EINVAL;
-
- klist_iter_init_node(&bus->p->klist_devices, &i,
- (start ? &start->p->knode_bus : NULL));
- while ((dev = next_device(&i)) && !error)
- error = fn(dev, data);
- klist_iter_exit(&i);
- return error;
- }
- EXPORT_SYMBOL_GPL(bus_for_each_dev);
通过klist将遍历该总线上的所有设备,并为其调用__driver_attach函数。
- static int __driver_attach(struct device *dev, void *data)
- {
- struct device_driver *drv = data;
-
-
-
- if (!driver_match_device(drv, dev))
- return 0;
-
- if (dev->parent)
- down(&dev->parent->sem);
- down(&dev->sem);
- if (!dev->driver)
- driver_probe_device(drv, dev);
- up(&dev->sem);
- if (dev->parent)
- up(&dev->parent->sem);
-
- return 0;
- }
首先调用了driver_match_device函数,该函数进会进行匹配,如果匹配成功将返回1。我们看下这个函数:
- static inline int driver_match_device(struct device_driver *drv,
- struct device *dev)
- {
- return drv->bus->match ? drv->bus->match(dev, drv) : 1;
- }
这里直接调用了platform总线的match方法,我们来看下这个方法。
- static int platform_match(struct device *dev, struct device_driver *drv)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct platform_driver *pdrv = to_platform_driver(drv);
-
-
- if (pdrv->id_table)
- return platform_match_id(pdrv->id_table, pdev) != NULL;
-
-
- return (strcmp(pdev->name, drv->name) == 0);
- }
该方法的核心其实就是使用stcmp进行字符匹配,判断pdev->name和drv->name是否相等。
在本例中两者同为s3c2410-spi。因此匹配完成,返回1。
返回后,由于dev->driver为NULL,将调用driver_probe_device函数。我们来看下:
- int driver_probe_device(struct device_driver *drv, struct device *dev)
- {
- int ret = 0;
-
- if (!device_is_registered(dev))
- return -ENODEV;
-
- pr_debug("bus: ‘%s‘: %s: matched device %s with driver %s\n",
- drv->bus->name, __func__, dev_name(dev), drv->name);
-
- ret = really_probe(dev, drv);
-
- return ret;
- }
- static inline int device_is_registered(struct device *dev)
- {
- return dev->kobj.state_in_sysfs;
- }
该函数将调用really_probe来绑定设备和它的驱动。
- static int really_probe(struct device *dev, struct device_driver *drv)
- {
- int ret = 0;
-
- atomic_inc(&probe_count);
- pr_debug("bus: ‘%s‘: %s: probing driver %s with device %s\n",
- drv->bus->name, __func__, drv->name, dev_name(dev));
- WARN_ON(!list_empty(&dev->devres_head));
-
- dev->driver = drv;
- if (driver_sysfs_add(dev)) {
- printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
- __func__, dev_name(dev));
- goto probe_failed;
- }
-
- if (dev->bus->probe) {
- ret = dev->bus->probe(dev);
- if (ret)
- goto probe_failed;
- } else if (drv->probe) {
- ret = drv->probe(dev);
- if (ret)
- goto probe_failed;
- }
-
- driver_bound(dev);
- ret = 1;
- pr_debug("bus: ‘%s‘: %s: bound device %s to driver %s\n",
- drv->bus->name, __func__, dev_name(dev), drv->name);
- goto done;
-
- probe_failed:
- devres_release_all(dev);
- driver_sysfs_remove(dev);
- dev->driver = NULL;
-
- if (ret != -ENODEV && ret != -ENXIO) {
-
- printk(KERN_WARNING
- "%s: probe of %s failed with error %d\n",
- drv->name, dev_name(dev), ret);
- }
-
- ret = 0;
- done:
- atomic_dec(&probe_count);
- wake_up(&probe_waitqueue);
- return ret;
- }
在这个函数中调用4个函数。
第一个函数driver_sysfs_add将更新sysfs。
- static int driver_sysfs_add(struct device *dev)
- {
- int ret;
-
- ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
- kobject_name(&dev->kobj));
- if (ret == 0) {
-
- ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
- "driver");
- if (ret)
- sysfs_remove_link(&dev->driver->p->kobj,
- kobject_name(&dev->kobj));
- }
- return ret;
- }
执行完以后,建立了两个链接。
在/sys/bus/platform/drivers/s3c2410-spi下建立链接,指向/sys/devices/platform/s3c2410-spi.0
在/sys/devices/platform/s3c2410-spi.0下建立链接,指向/sys/devices/platform/s3c2410-spi.0。
这样就在用户空间呈现出驱动和设备的关系了。我们来验证下。
[root@yj423 s3c2410-spi]#pwd
/sys/bus/platform/drivers/s3c2410-spi
[root@yj423 s3c2410-spi]#ll s3c2410-spi.0
lrwxrwxrwx 1 root root 0 Jan 1 02:28 s3c2410-spi.0 -> ../../../../devices/platform/s3c2410-spi.0
[root@yj423 s3c2410-spi.0]#pwd
/sys/devices/platform/s3c2410-spi.0
[root@yj423 s3c2410-spi.0]#ll driver
lrwxrwxrwx 1 root root 0 Jan 1 02:26 driver -> ../../../bus/platform/drivers/s3c2410-spi
第2个函数执行总线的probe方法,由于platform总线没有提供probe方法,因此不执行。
第3个函数执行驱动的probe方法,驱动提供了probe,因此调用它,该函数的细节超过了本文的讨论内容,所以略过。
第4个函数执行driver_bound,用来绑定设备和驱动,来看下这个函数。
- static void driver_bound(struct device *dev)
- {
- if (klist_node_attached(&dev->p->knode_driver)) {
- printk(KERN_WARNING "%s: device %s already bound\n",
- __func__, kobject_name(&dev->kobj));
- return;
- }
-
- pr_debug("driver: ‘%s‘: %s: bound to device ‘%s‘\n", dev_name(dev),
- __func__, dev->driver->name);
-
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_BOUND_DRIVER, dev);
-
- klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
- }
其实,所谓的绑定,就是将设备的驱动节点添加到驱动支持的设备链表中。
至此,通过内核链表,这个platform device 和platform driver 已经绑定完成,将继续遍历内核链表尝试匹配和绑定,直到链表结束。
在driver_attach执行完毕以后,bus_add_driver函数还有些剩余工作要完成。
首先,将驱动添加到总线的驱动列表中。
接着,如果定义了驱动属性文件,则创建。
最后,在/sys/bus/platform/drivers/s3c2410-spi/下建立属性文件uevent,并在同一目录下建立文件bind和unbind。
我们来验证下:
[root@yj423 s3c2410-spi]#pwd
/sys/bus/platform/drivers/s3c2410-spi
[root@yj423 s3c2410-spi]#ls
bind s3c2410-spi.0 uevent unbind
7.7 小结
在本节中,我们看到了platform driver是如何注册到内核中,在注册过程中,通过更新了sysfs,向用户空间展示总线,设备和驱动之间的关系。
同时,还更新了链表的指向,在内核中体现了同样的关系。
最后以platform driver的注册过程结束本章。
8. sysfs底层函数
下面讲述的内容将基于VFS,有关VFS的基本内容超过本文的范围,请参考<<深入理解Linux内核>>一书的第12章。
在前面讲述的过程中,我们知道设备驱动模型是如何通过kobject将总线,设备和驱动间的层次关系在用户空间呈现出来的。事实上,就是通过目录,文件和symlink来呈现相互之间的关系。在前面的叙述中,我们并没有对目录,文件和symlink的创建进行 讲解,本章就对这些底层函数进行讲解。在讲解这些函数之前,我们先来看下,sysfs文件系统是如何注册的。
8.1 注册sysfs文件系统
sysfs文件系统的注册是调用sysfs_init函数来完成的,该函数在内核启动阶段被调用,我们来看下大致函数调用流程,这里不作分析。
start_kernel( ) -> vfs_caches_init( ) -> mnt_init( ) -> mnt_init( ) -> sysfs_init( )。
- int __init sysfs_init(void)
- {
- int err = -ENOMEM;
-
- sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache",
- sizeof(struct sysfs_dirent),
- 0, 0, NULL);
- if (!sysfs_dir_cachep)
- goto out;
-
- err = sysfs_inode_init();
- if (err)
- goto out_err;
-
- err = register_filesystem(&sysfs_fs_type);
- if (!err) {
-
- sysfs_mount = kern_mount(&sysfs_fs_type);
- if (IS_ERR(sysfs_mount)) {
- printk(KERN_ERR "sysfs: could not mount!\n");
- err = PTR_ERR(sysfs_mount);
- sysfs_mount = NULL;
- unregister_filesystem(&sysfs_fs_type);
- goto out_err;
- }
- } else
- goto out_err;
- out:
- return err;
- out_err:
- kmem_cache_destroy(sysfs_dir_cachep);
- sysfs_dir_cachep = NULL;
- goto out;
- }
-
- static struct file_system_type sysfs_fs_type = {
- .name = "sysfs",
- .get_sb = sysfs_get_sb,
- .kill_sb = kill_anon_super,
- };
8.1.1 register_filesystem
下列代码位于fs/filesystems.c。
-
- int register_filesystem(struct file_system_type * fs)
- {
- int res = 0;
- struct file_system_type ** p;
-
- BUG_ON(strchr(fs->name, ‘.‘));
- if (fs->next)
- return -EBUSY;
- INIT_LIST_HEAD(&fs->fs_supers);
- write_lock(&file_systems_lock);
- p = find_filesystem(fs->name, strlen(fs->name));
- if (*p)
- res = -EBUSY;
- else
- *p = fs;
- write_unlock(&file_systems_lock);
- return res;
- }
- static struct file_system_type **find_filesystem(const char *name, unsigned len)
- {
- struct file_system_type **p;
- for (p=&file_systems; *p; p=&(*p)->next)
- if (strlen((*p)->name) == len &&
- strncmp((*p)->name, name, len) == 0)
- break;
- return p;
- }
该函数将调用函数file_system_type,此函数根据name字段(sysfs)来查找要注册的文件系统是否已经存在。
如果不存在,表示还未注册,则将新的fs添加到链表中,链表的第一项为全局变量file_systems。
该全局变量为单项链表,所有已注册的文件系统都被插入到这个链表当中。
8.1.2 kern_mount函数
下列代码位于include/linux/fs.h
- #define kern_mount(type) kern_mount_data(type, NULL)
下列代码位于fs/sysfs/mount.c
- struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
- {
- return vfs_kern_mount(type, MS_KERNMOUNT, type->name, data);
- }
-
- EXPORT_SYMBOL_GPL(kern_mount_data);
kern_mount实际上最后是调用了vfs_kern_mount函数。我们来看下:
- struct vfsmount *
- vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
- {
- struct vfsmount *mnt;
- char *secdata = NULL;
- int error;
-
- if (!type)
- return ERR_PTR(-ENODEV);
-
- error = -ENOMEM;
- mnt = alloc_vfsmnt(name);
- if (!mnt)
- goto out;
-
- if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
- secdata = alloc_secdata();
- if (!secdata)
- goto out_mnt;
-
- error = security_sb_copy_data(data, secdata);
- if (error)
- goto out_free_secdata;
- }
-
- error = type->get_sb(type, flags, name, data, mnt);
- if (error < 0)
- goto out_free_secdata;
- BUG_ON(!mnt->mnt_sb);
-
- error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);
- if (error)
- goto out_sb;
-
- mnt->mnt_mountpoint = mnt->mnt_root;
- mnt->mnt_parent = mnt;
- up_write(&mnt->mnt_sb->s_umount);
- free_secdata(secdata);
- return mnt;
- out_sb:
- dput(mnt->mnt_root);
- deactivate_locked_super(mnt->mnt_sb);
- out_free_secdata:
- free_secdata(secdata);
- out_mnt:
- free_vfsmnt(mnt);
- out:
- return ERR_PTR(error);
- }
该函数在首先调用alloc_vfsmnt来分配struct vfsmount结构,并做了一些初试化工作。
下列函数位于fs/super.c
- struct vfsmount *alloc_vfsmnt(const char *name)
- {
- struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
- if (mnt) {
- int err;
-
- err = mnt_alloc_id(mnt);
- if (err)
- goto out_free_cache;
-
- if (name) {
- mnt->mnt_devname = kstrdup(name, GFP_KERNEL);
- if (!mnt->mnt_devname)
- goto out_free_id;
- }
-
- atomic_set(&mnt->mnt_count, 1);
- INIT_LIST_HEAD(&mnt->mnt_hash);
- INIT_LIST_HEAD(&mnt->mnt_child);
- INIT_LIST_HEAD(&mnt->mnt_mounts);
- INIT_LIST_HEAD(&mnt->mnt_list);
- INIT_LIST_HEAD(&mnt->mnt_expire);
- INIT_LIST_HEAD(&mnt->mnt_share);
- INIT_LIST_HEAD(&mnt->mnt_slave_list);
- INIT_LIST_HEAD(&mnt->mnt_slave);
- atomic_set(&mnt->__mnt_writers, 0);
- }
- return mnt;
-
- out_free_id:
- mnt_free_id(mnt);
- out_free_cache:
- kmem_cache_free(mnt_cache, mnt);
- return NULL;
- }
分配好结构体以后,由于参数data为NULL,将直接调用文件系统类型提供的get_sb方法,该方法就是函数sysfs_get_sb。我们来看下:
下列函数位于fs/sysfs/mount.c。
- static int sysfs_get_sb(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data, struct vfsmount *mnt)
- {
- return get_sb_single(fs_type, flags, data, sysfs_fill_super, mnt);
- }
这里直接调用了get_sb_single函数,注意这里的第4个实参sysfs_fill_super,该参数是函数名,后面将会调用该函数。
该函数将分配sysfs文件系统的superblock,获取文件系统根目录的inode和dentry。
该函数的执行过程相当复杂,在下一节单独讲述。
8.2 get_sb_single函数
下列函数位于fs/sysfs/mount.c。
- int get_sb_single(struct file_system_type *fs_type,
- int flags, void *data,
- int (*fill_super)(struct super_block *, void *, int),
- struct vfsmount *mnt)
- {
- struct super_block *s;
- int error;
-
- s = sget(fs_type, compare_single, set_anon_super, NULL);
- if (IS_ERR(s))
- return PTR_ERR(s);
- if (!s->s_root) {
- s->s_flags = flags;
-
- error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
- if (error) {
- deactivate_locked_super(s);
- return error;
- }
- s->s_flags |= MS_ACTIVE;
- }
- do_remount_sb(s, flags, data, 0);
- simple_set_mnt(mnt, s);
- return 0;
- }
-
- EXPORT_SYMBOL(get_sb_single);
8.2.1 sget函数
首先调用了sget函数来查找是否
下列函数位于fs/super.c。
- struct super_block *sget(struct file_system_type *type,
- int (*test)(struct super_block *,void *),
- int (*set)(struct super_block *,void *),
- void *data)
- {
- struct super_block *s = NULL;
- struct super_block *old;
- int err;
-
- retry:
- spin_lock(&sb_lock);
- if (test) {
-
- list_for_each_entry(old, &type->fs_supers, s_instances) {
- if (!test(old, data))
- continue;
- if (!grab_super(old))
- goto retry;
- if (s) {
- up_write(&s->s_umount);
- destroy_super(s);
- }
- return old;
- }
- }
- if (!s) {
- spin_unlock(&sb_lock);
- s = alloc_super(type);
- if (!s)
- return ERR_PTR(-ENOMEM);
- goto retry;
- }
-
- err = set(s, data);
- if (err) {
- spin_unlock(&sb_lock);
- up_write(&s->s_umount);
- destroy_super(s);
- return ERR_PTR(err);
- }
- s->s_type = type;
- strlcpy(s->s_id, type->name, sizeof(s->s_id));
- list_add_tail(&s->s_list, &super_blocks);
- list_add(&s->s_instances, &type->fs_supers);
- spin_unlock(&sb_lock);
- get_filesystem(type);
- return s;
- }
-
- EXPORT_SYMBOL(sget);
该函数将遍历属于sysfs文件系统的所有superblock,本例中由于之前没有任何superblock创建,遍历立即结束。
然后调用alloc_super函数来创建新的struct super_block。
下列函数位于fs/super.c。
- static struct super_block *alloc_super(struct file_system_type *type)
- {
- struct super_block *s = kzalloc(sizeof(struct super_block), GFP_USER);
- static struct super_operations default_op;
-
- if (s) {
- if (security_sb_alloc(s)) {
- kfree(s);
- s = NULL;
- goto out;
- }
- INIT_LIST_HEAD(&s->s_dirty);
- INIT_LIST_HEAD(&s->s_io);
- INIT_LIST_HEAD(&s->s_more_io);
- INIT_LIST_HEAD(&s->s_files);
- INIT_LIST_HEAD(&s->s_instances);
- INIT_HLIST_HEAD(&s->s_anon);
- INIT_LIST_HEAD(&s->s_inodes);
- INIT_LIST_HEAD(&s->s_dentry_lru);
- INIT_LIST_HEAD(&s->s_async_list);
- init_rwsem(&s->s_umount);
- mutex_init(&s->s_lock);
- lockdep_set_class(&s->s_umount, &type->s_umount_key);
-
- lockdep_set_class(&s->s_lock, &type->s_lock_key);
-
- down_write_nested(&s->s_umount, SINGLE_DEPTH_NESTING);
- s->s_count = S_BIAS;
- atomic_set(&s->s_active, 1);
- mutex_init(&s->s_vfs_rename_mutex);
- mutex_init(&s->s_dquot.dqio_mutex);
- mutex_init(&s->s_dquot.dqonoff_mutex);
- init_rwsem(&s->s_dquot.dqptr_sem);
- init_waitqueue_head(&s->s_wait_unfrozen);
- s->s_maxbytes = MAX_NON_LFS;
- s->dq_op = sb_dquot_ops;
- s->s_qcop = sb_quotactl_ops;
- s->s_op = &default_op;
- s->s_time_gran = 1000000000;
- }
- out:
- return s;
- }
分配完以后,调用作为参数传入的函数指针set,也就是set_anon_super函数,该函数用来设置s->s_dev。
下列函数位于fs/super.c。
- int set_anon_super(struct super_block *s, void *data)
- {
- int dev;
- int error;
-
- retry:
- if (ida_pre_get(&unnamed_dev_ida, GFP_ATOMIC) == 0)
- return -ENOMEM;
- spin_lock(&unnamed_dev_lock);
- error = ida_get_new(&unnamed_dev_ida, &dev);
- spin_unlock(&unnamed_dev_lock);
- if (error == -EAGAIN)
-
- goto retry;
- else if (error)
- return -EAGAIN;
-
- if ((dev & MAX_ID_MASK) == (1 << MINORBITS)) {
- spin_lock(&unnamed_dev_lock);
- ida_remove(&unnamed_dev_ida, dev);
- spin_unlock(&unnamed_dev_lock);
- return -EMFILE;
- }
- s->s_dev = MKDEV(0, dev & MINORMASK);
- return 0;
- }
8.2.2 sysfs_fill_super函数
分配了super_block之后,将判断该super_block是否有root dentry。本例中,显然没有。然后调用形参fill_super指向的函数,也就是sysfs_fill_super函数。
下列函数位于fs/sysfs/mount.c。
- struct super_block * sysfs_sb = NULL;
-
- static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
- {
- struct inode *inode;
- struct dentry *root;
-
- sb->s_blocksize = PAGE_CACHE_SIZE;
- sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
- sb->s_magic = SYSFS_MAGIC;
- sb->s_op = &sysfs_ops;
- sb->s_time_gran = 1;
- sysfs_sb = sb;
-
-
- mutex_lock(&sysfs_mutex);
- inode = sysfs_get_inode(&sysfs_root);
- mutex_unlock(&sysfs_mutex);
- if (!inode) {
- pr_debug("sysfs: could not get root inode\n");
- return -ENOMEM;
- }
-
-
- root = d_alloc_root(inode);
- if (!root) {
- pr_debug("%s: could not get root dentry!\n",__func__);
- iput(inode);
- return -ENOMEM;
- }
- root->d_fsdata = &sysfs_root;
- sb->s_root = root;
- return 0;
- }
-
- struct sysfs_dirent sysfs_root = {
- .s_name = "",
- .s_count = ATOMIC_INIT(1),
- .s_flags = SYSFS_DIR,
- .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
- .s_ino = 1,
- };
在设置了一些字段后,设置了sysfs_sb这个全局变量,该全局变量表示的就是sysfs的super_block。
随后,调用了sysfs_get_inode函数,来获取sysfs的根目录的dirent。该函数的参数sysfs_root为全局变量,表示sysfs的根目录的sysfs_dirent。
我们看些这个sysfs_dirent数据结构:
- 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;
- };
其中比较关键的就是那个联合体,针对不同的形式(目录,symlink,属性文件和可执行文件)将使用不同的数据结构。
另外,sysfs_dirent将最为dentry的fs专有数据被保存下来,这一点会在下面中看到。
接着,在来看下sysfs_get_inode函数:
下列函数位于fs/sysfs/inode.c。
- struct inode * sysfs_get_inode(struct sysfs_dirent *sd)
- {
- struct inode *inode;
-
- inode = iget_locked(sysfs_sb, sd->s_ino);
- if (inode && (inode->i_state & I_NEW))
- sysfs_init_inode(sd, inode);
-
- return inode;
- }
-
- struct inode *iget_locked(struct super_block *sb, unsigned long ino)
- {
- struct hlist_head *head = inode_hashtable + hash(sb, ino);
- struct inode *inode;
-
- inode = ifind_fast(sb, head, ino);
- if (inode)
- return inode;
-
- return get_new_inode_fast(sb, head, ino);
- }
- EXPORT_SYMBOL(iget_locked);
-
- static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
- {
- struct bin_attribute *bin_attr;
-
- inode->i_private = sysfs_get(sd);
- inode->i_mapping->a_ops = &sysfs_aops;
- inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
- inode->i_op = &sysfs_inode_operations;
- inode->i_ino = sd->s_ino;
- lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key);
-
- if (sd->s_iattr) {
-
- set_inode_attr(inode, sd->s_iattr);
- } else
- set_default_inode_attr(inode, sd->s_mode);
-
-
-
- switch (sysfs_type(sd)) {
- case SYSFS_DIR:
- inode->i_op = &sysfs_dir_inode_operations;
- inode->i_fop = &sysfs_dir_operations;
- inode->i_nlink = sysfs_count_nlink(sd);
- break;
- case SYSFS_KOBJ_ATTR:
- inode->i_size = PAGE_SIZE;
- inode->i_fop = &sysfs_file_operations;
- break;
- case SYSFS_KOBJ_BIN_ATTR:
- bin_attr = sd->s_bin_attr.bin_attr;
- inode->i_size = bin_attr->size;
- inode->i_fop = &bin_fops;
- break;
- case SYSFS_KOBJ_LINK:
- inode->i_op = &sysfs_symlink_inode_operations;
- break;
- default:
- BUG();
- }
-
- unlock_new_inode(inode);
- }
该函数首先调用了,iget_locked来查找该inode是否已存在,如果不存在则创建。如果是新创建的inode,则对inode进行初始化。
再获取了根目录的inode和sysfs_dirent后,调用d_alloc_root来获得dirent。
-
- struct dentry * d_alloc_root(struct inode * root_inode)
- {
- struct dentry *res = NULL;
-
- if (root_inode) {
- static const struct qstr name = { .name = "/", .len = 1 };
-
- res = d_alloc(NULL, &name);
- if (res) {
- res->d_sb = root_inode->i_sb;
- res->d_parent = res;
- d_instantiated_instantiate(res, root_inode);
- }
- }
- return res;
- }
-
-
- struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
- {
- struct dentry *dentry;
- char *dname;
-
- dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);
- if (!dentry)
- return NULL;
-
- if (name->len > DNAME_INLINE_LEN-1) {
- dname = kmalloc(name->len + 1, GFP_KERNEL);
- if (!dname) {
- kmem_cache_free(dentry_cache, dentry);
- return NULL;
- }
- } else {
- dname = dentry->d_iname;
- }
- dentry->d_name.name = dname;
-
- dentry->d_name.len = name->len;
- dentry->d_name.hash = name->hash;
- memcpy(dname, name->name, name->len);
- dname[name->len] = 0;
-
- atomic_set(&dentry->d_count, 1);
- dentry->d_flags = DCACHE_UNHASHED;
- spin_lock_init(&dentry->d_lock);
- dentry->d_inode = NULL;
- dentry->d_parent = NULL;
- dentry->d_sb = NULL;
- dentry->d_op = NULL;
- dentry->d_fsdata = NULL;
- dentry->d_mounted = 0;
- INIT_HLIST_NODE(&dentry->d_hash);
- INIT_LIST_HEAD(&dentry->d_lru);
- INIT_LIST_HEAD(&dentry->d_subdirs);
- INIT_LIST_HEAD(&dentry->d_alias);
-
- if (parent) {
- dentry->d_parent = dget(parent);
- dentry->d_sb = parent->d_sb;
- } else {
- INIT_LIST_HEAD(&dentry->d_u.d_child);
- }
-
- spin_lock(&dcache_lock);
- if (parent)
- list_add(&dentry->d_u.d_child, &parent->d_subdirs);
- dentry_stat.nr_dentry++;
- spin_unlock(&dcache_lock);
-
- return dentry;
- }
-
-
- void d_instantiate(struct dentry *entry, struct inode * inode)
- {
- BUG_ON(!list_empty(&entry->d_alias));
- spin_lock(&dcache_lock);
- __d_instantiate(entry, inode);
- spin_unlock(&dcache_lock);
- security_d_instantiate(entry, inode);
- }
-
- static void __d_instantiate(struct dentry *dentry, struct inode *inode)
- {
- if (inode)
- list_add(&dentry->d_alias, &inode->i_dentry);
- dentry->d_inode = inode;
- fsnotify_d_instantiate(dentry, inode);
- }
该函数首先调用了d_alloc来创建struct dentry,参数parent为NULL,既然是为根( / )建立dentry,自然没有父对象。
接着调用d_instantiate来绑定inode和dentry之间的关系。
在sysfs_fill_super函数执行的最后,将sysfs_root保存到了dentry->d_fsdata。
可见,在sysfs中用sysfs_dirent来表示目录,但是对于VFS,还是要使用dentry来表示目录。
8.2.3 do_remount_sb
下列代码位于fs/super.c。
- int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
- {
- int retval;
- int remount_rw;
-
- #ifdef CONFIG_BLOCK
- if (!(flags & MS_RDONLY) && bdev_read_only(sb->s_bdev))
- return -EACCES;
- #endif
- if (flags & MS_RDONLY)
- acct_auto_close(sb);
- shrink_dcache_sb(sb);
- fsync_super(sb);
-
-
- if ((flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY)) {
- if (force)
- mark_files_ro(sb);
- else if (!fs_may_remount_ro(sb))
- return -EBUSY;
- retval = vfs_dq_off(sb, 1);
- if (retval < 0 && retval != -ENOSYS)
- return -EBUSY;
- }
- remount_rw = !(flags & MS_RDONLY) && (sb->s_flags & MS_RDONLY);
-
- if (sb->s_op->remount_fs) {
- lock_super(sb);
- retval = sb->s_op->remount_fs(sb, &flags, data);
- unlock_super(sb);
- if (retval)
- return retval;
- }
- sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK);
- if (remount_rw)
- vfs_dq_quota_on_remount(sb);
- return 0;
- }
这个函数用来修改挂在选项,这个函数就不分析了,不是重点。
8.2.4simple_set_mnt
下列函数位于fs/namespace.c。
- void simple_set_mnt(struct vfsmount *mnt, struct super_block *sb)
- {
- mnt->mnt_sb = sb;
- mnt->mnt_root = dget(sb->s_root);
- }
该函数设置了vfsmount的superblock和根dentry。
8.2.5 小结
这里,对sysfs的注册过程做一个总结。
sysfs_init函数调用过程示意图如下:
在整个过程中,先后使用和创建了许多struct
第一,根据file_system_type表示的sysfs文件系统的类型注册了sysfs。
第二,建立了vfsmount。
第三,创建了超级块super_block。
第四,根据sysfs_dirent表示的根目录,建立了inode。
最后,根据刚才建立的inode创建了dentry。
除了sysfs_dirent,其他5个结构体都是VFS中基本的数据结构,而sysfs_dirent则是特定于sysfs文件系统的数据结构。
8.3 创建目录
在前面的描述中,使用sysfs_create_dir在sysfs下建立一个目录。我们来看下这个函数是如何来建立目录的。
下列代码位于fs/sysfs/dir.c。
- int sysfs_create_dir(struct kobject * kobj)
- {
- struct sysfs_dirent *parent_sd, *sd;
- int error = 0;
-
- BUG_ON(!kobj);
-
- if (kobj->parent)
- parent_sd = kobj->parent->sd;
- else
- parent_sd = &sysfs_root;
-
- error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);
- if (!error)
- kobj->sd = sd;
- return error;
- }
函数中,首先获取待建目录的父sysfs_dirent,然后将它作为参数 来调用create_dir函数。
很明显,就是要在父sysfs_dirent下建立新的sysfs_dirent,新建立的sysfs_dirent将保存到参数sd中。
下列代码位于fs/sysfs/dir.c。
- static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
- const char *name, struct sysfs_dirent **p_sd)
- {
- umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
- struct sysfs_addrm_cxt acxt;
- struct sysfs_dirent *sd;
- int rc;
-
-
- sd = sysfs_new_dirent(name, mode, SYSFS_DIR);
- if (!sd)
- return -ENOMEM;
- sd->s_dir.kobj = kobj;
-
-
- sysfs_addrm_start(&acxt, parent_sd);
- rc = sysfs_add_one(&acxt, sd);
- sysfs_addrm_finish(&acxt);
-
- if (rc == 0)
- *p_sd = sd;
- else
- sysfs_put(sd);
-
- return rc;
- }
这里要注意一下mode变量,改变了使用了宏定义SYSFS_DIR,这个就表示要创建的是一个目录。
mode还有几个宏定义可以使用,如下:
- #define SYSFS_KOBJ_ATTR 0x0002
- #define SYSFS_KOBJ_BIN_ATTR 0x0004
- #define SYSFS_KOBJ_LINK 0x0008
- #define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK)
8.3.1 sysfs_new_dirent
在create_dir函数中,首先调用了sysfs_new_dirent来建立一个新的sysfs_dirent结构体。
下列代码位于fs/sysfs/dir.c。
- struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
- {
- char *dup_name = NULL;
- struct sysfs_dirent *sd;
-
- if (type & SYSFS_COPY_NAME) {
- name = dup_name = kstrdup(name, GFP_KERNEL);
- if (!name)
- return NULL;
- }
-
- sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL);
- if (!sd)
- goto err_out1;
-
- if (sysfs_alloc_ino(&sd->s_ino))
- goto err_out2;
-
- atomic_set(&sd->s_count, 1);
- atomic_set(&sd->s_active, 0);
-
- sd->s_name = name;
- sd->s_mode = mode;
- sd->s_flags = type;
-
- return sd;
-
- err_out2:
- kmem_cache_free(sysfs_dir_cachep, sd);
- err_out1:
- kfree(dup_name);
- return NULL;
- }
8.3.2 有关sysfs_dirent中的联合体
分配了sysfs_dirent后,设置了该结构中的联合体数据。先来看下联合体中的四个数据结构。
- struct sysfs_elem_dir {
- struct kobject *kobj;
-
- struct sysfs_dirent *children;
- };
-
- struct sysfs_elem_symlink {
- struct sysfs_dirent *target_sd;
- };
-
- struct sysfs_elem_attr {
- struct attribute *attr;
- struct sysfs_open_dirent *open;
- };
-
- struct sysfs_elem_bin_attr {
- struct bin_attribute *bin_attr;
- struct hlist_head buffers;
- };
根据sysfs_dirent所代表的类型不同,也就是目录,synlink,属性文件和bin文件,将分别使用该联合体中相应的struct。
在本例中要创建的是目录,自然使用sysfs_elem_dir结构体,然后保存了kobject对象。
在8.4和8.5中我们将分别看到sysfs_elem_attr和sysfs_elem_symlink的使用。
8.3.3 sysfs_addrm_start
在获取了父sysfs_dirent,调用sysfs_addrm_start来获取与之对应的inode。
下列代码位于fs/sysfs/dir.c。
- void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
- struct sysfs_dirent *parent_sd)
- {
- struct inode *inode;
-
- memset(acxt, 0, sizeof(*acxt));
- acxt->parent_sd = parent_sd;
-
-
- mutex_lock(&sysfs_mutex);
-
- inode = ilookup5(sysfs_sb, parent_sd->s_ino, sysfs_ilookup_test,
- parent_sd);
- if (inode) {
- WARN_ON(inode->i_state & I_NEW);
-
-
- acxt->parent_inode = inode;
-
-
- if (!mutex_trylock(&inode->i_mutex)) {
- mutex_unlock(&sysfs_mutex);
- mutex_lock(&inode->i_mutex);
- mutex_lock(&sysfs_mutex);
- }
- }
- }
-
- struct sysfs_addrm_cxt {
- struct sysfs_dirent *parent_sd;
- struct inode *parent_inode;
- struct sysfs_dirent *removed;
- int cnt;
- };
注意形参sysfs_addrm_cxt,该结构作用是临时存放数据。
8.3.4 sysfs_add_one
下列代码位于fs/sysfs/dir.c。
- int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
- {
- int ret;
-
- ret = __sysfs_add_one(acxt, sd);
- if (ret == -EEXIST) {
- char *path = kzalloc(PATH_MAX, GFP_KERNEL);
- WARN(1, KERN_WARNING
- "sysfs: cannot create duplicate filename ‘%s‘\n",
- (path == NULL) ? sd->s_name :
- strcat(strcat(sysfs_pathname(acxt->parent_sd, path), "/"),
- sd->s_name));
- kfree(path);
- }
-
- return ret;
- }
-
- int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
- {
-
- if (sysfs_find_dirent(acxt->parent_sd, sd->s_name))
- return -EEXIST;
-
- sd->s_parent = sysfs_get(acxt->parent_sd);
-
- if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode)
- inc_nlink(acxt->parent_inode);
-
- acxt->cnt++;
-
- sysfs_link_sibling(sd);
-
- return 0;
- }
-
- struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
- const unsigned char *name)
- {
- struct sysfs_dirent *sd;
-
- for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling)
- if (!strcmp(sd->s_name, name))
- return sd;
- return NULL;
- }
-
- static void sysfs_link_sibling(struct sysfs_dirent *sd)
- {
- struct sysfs_dirent *parent_sd = sd->s_parent;
- struct sysfs_dirent **pos;
-
- BUG_ON(sd->s_sibling);
-
-
-
- for (pos = &parent_sd->s_dir.children; *pos; pos = &(*pos)->s_sibling) {
- if (sd->s_ino < (*pos)->s_ino)
- break;
- }
-
- sd->s_sibling = *pos;
- *pos = sd;
- }
该函数直接调用了__sysfs_add_one,后者先调用sysfs_find_dirent来查找该parent_sd下有无该的sysfs_dirent,如果没有,则设置创建好的新的sysfs_dirent的s_parent字段。也就是将新的sysfs_dirent添加到父sys_dirent中。接着调用sysfs_link_sibling函数,将新建的sysfs_dirent添加到sd->s_parent->s_dir.children链表中。
8.3.5 sysfs_addrm_finish
下列代码位于fs/sysfs/dir.c。
- void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
- {
-
- mutex_unlock(&sysfs_mutex);
- if (acxt->parent_inode) {
- struct inode *inode = acxt->parent_inode;
-
-
- if (acxt->cnt)
- inode->i_ctime = inode->i_mtime = CURRENT_TIME;
-
- mutex_unlock(&inode->i_mutex);
- iput(inode);
- }
-
-
- while (acxt->removed) {
- struct sysfs_dirent *sd = acxt->removed;
-
- acxt->removed = sd->s_sibling;
- sd->s_sibling = NULL;
-
- sysfs_drop_dentry(sd);
- sysfs_deactivate(sd);
- unmap_bin_file(sd);
- sysfs_put(sd);
- }
- }
该函数结束了添加sysfs_dirent的工作,这个就不多做说明了。
至此,添加一个目录的工作已经完成了,添加目录的工作其实就是创建了一个新的sysfs_dirent,并把它添加到父sysfs_dirent中。
下面我们看下如何添加属性文件。
8.4 创建属性文件
添加属性文件使用sysfs_create_file函数。
下列函数位于fs/sysfs/file.c。
-
- int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
- {
- BUG_ON(!kobj || !kobj->sd || !attr);
-
- return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);
-
- }
-
- int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
- int type)
- {
- return sysfs_add_file_mode(dir_sd, attr, type, attr->mode);
- }
-
- int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,
- const struct attribute *attr, int type, mode_t amode)
- {
- umode_t mode = (amode & S_IALLUGO) | S_IFREG;
- struct sysfs_addrm_cxt acxt;
- struct sysfs_dirent *sd;
- int rc;
-
- sd = sysfs_new_dirent(attr->name, mode, type);
- if (!sd)
- return -ENOMEM;
- sd->s_attr.attr = (void *)attr;
-
- sysfs_addrm_start(&acxt, dir_sd);
- rc = sysfs_add_one(&acxt, sd);
- sysfs_addrm_finish(&acxt);
-
- if (rc)
- sysfs_put(sd);
-
- return rc;
- }
sysfs_create_file用参数SYSFS_KOBJ_ATTR(表示建立属性文件)来调用了sysfs_add_file,后者又直接调用了sysfs_add_file_mode。
sysfs_add_file_mode函数的执行和8.3节的create_dir函数非常类似,只不过它并没有保存kobject对象,也就是说该sysfs_dirent并没有一个对应的kobject对象。
需要注意的是,这里是建立属性文件,因此使用了联合体中的结构体s_attr。
8.5 创建symlink
最后,来看下symlink的建立。
- int sysfs_create_link(struct kobject *kobj, struct kobject *target,
- const char *name)
- {
- return sysfs_do_create_link(kobj, target, name, 1);
- }
-
- static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target,
- const char *name, int warn)
- {
- struct sysfs_dirent *parent_sd = NULL;
- struct sysfs_dirent *target_sd = NULL;
- struct sysfs_dirent *sd = NULL;
- struct sysfs_addrm_cxt acxt;
- int error;
-
- BUG_ON(!name);
-
- if (!kobj)
- parent_sd = &sysfs_root;
- else
- parent_sd = kobj->sd;
-
- error = -EFAULT;
- if (!parent_sd)
- goto out_put;
-
-
- spin_lock(&sysfs_assoc_lock);
- if (target->sd)
- target_sd = sysfs_get(target->sd); 、
- spin_unlock(&sysfs_assoc_lock);
-
- error = -ENOENT;
- if (!target_sd)
- goto out_put;
-
- error = -ENOMEM;
-
- sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK);
- if (!sd)
- goto out_put;
-
- sd->s_symlink.target_sd = target_sd;
- target_sd = NULL;
-
- sysfs_addrm_start(&acxt, parent_sd);
- if (warn)
- error = sysfs_add_one(&acxt, sd);
- else
- error = __sysfs_add_one(&acxt, sd);
- sysfs_addrm_finish(&acxt);
-
- if (error)
- goto out_put;
-
- return 0;
-
- out_put:
- sysfs_put(target_sd);
- sysfs_put(sd);
- return error;
- }
这个函数的执行也和8.3节的create_dir函数非常类似。其次,symlink同样没有对应的kobject对象。
因为sysfs_dirent表示的是symlink,这里使用了联合体中的s_symlink。同时设置了s_symlink.target_sd指向的目标sysfs_dirent为参数targed_sd。
8.6 小结
本节首先对syfs这一特殊的文件系统的注册过程进行了分析。接着对目录,属性文件和symlink的建立进行了分析。这三者的建立过程基本一致,但是目录
有kobject对象,而剩余两个没有。其次,这三者的每个sysfs_dirent中,都使用了自己的联合体数据。
9 总结
本文首先对sysfs的核心数据kobject,kset等数据结构做出了分析,正是通过它们才能向用户空间呈现出设备驱动模型。
接着,以/sys/bus目录的建立为例,来说明如何通过kobject和kset来建立该bus目录。
随后,介绍了驱动模型中表示总线,设备和驱动的三个数据结构。
然后,介绍了platform总线(bus/platform)的注册,再介绍了虚拟的platform设备(devices/platform)的添加过程。
之后 ,以spi主控制器的platform设备为例,介绍了该platform设备和相应的驱动的注册过程。
最后,介绍了底层sysfs文件系统的注册过程和如何建立目录,属性文件和symlink的过程。
更新说明:
2012.09.14 在6.2.9中,添加分析 bus_for_each_drv。
转:http://blog.csdn.net/yj4231/article/details/7799245
Linux设备模型——设备驱动模型和sysfs文件系统解读
标签:
原文地址:http://www.cnblogs.com/Ph-one/p/5052191.html