设备模型中,关心总线,设备,驱动这三个实体,总线将设备和驱动绑定,在系统每注册一个设备的时候,会寻找与之匹配的驱动。
相反,在系统每注册一个驱动的时候,寻找与之匹配的设备,匹配是由总线来完成的。本文分析总线的初始化,即bus_kset的建立过程。
bus_kset是基础,其他总线如platform,SPI,I2C初始化时都会调用bus_register()进行总线注册,他们都会将自己的父kset设置为bus_kset。
kobj->state_in_sysfs设置为1。
kobj->ktype->default_attrs的值来判断是否需要创建文件。因为kobj->ktype->default_attrs代表着目
录下文件的信息。
先来分析sysfs_create_dir()
该函数此时也为空。以后会遇到不为空的情况,到时再分析。
通过以上分析,可以认为创建目录的实质是创建一个sysfs_dirent结构,并建立和父目录,兄弟目录,子文件以及对应kobj之间的关联。
相反,在系统每注册一个驱动的时候,寻找与之匹配的设备,匹配是由总线来完成的。本文分析总线的初始化,即bus_kset的建立过程。
bus_kset是基础,其他总线如platform,SPI,I2C初始化时都会调用bus_register()进行总线注册,他们都会将自己的父kset设置为bus_kset。
他们的关系也可以通过/sys/bus目录结构体现出来。
# ls /sys/bus
hid i2c mdio_bus platform spi
本文主要参考文章是
http://blog.chinaunix.net/uid-13321460-id-2902418.html
,为加深理解,也参照该文章重新画了几张图。
总线的初始化的初始化由buses_init()完成,
start_kernel()
->rest_init()
->kernel_init()
->do_basic_setup()
->driver_init()
->buses_init()
int __init buses_init(void)
{
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
if (!bus_kset)
return -ENOMEM;
return 0;
}
struct kset *kset_create_and_add(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int error;
/*1创建kset*/
kset = kset_create(name, uevent_ops, parent_kobj);
if (!kset)
return NULL;
/*2注册kset*/
error = kset_register(kset);
if (error) {
kfree(kset);
return NULL;
}
return kset;
}
1.创建kset
static struct kset *kset_create(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int retval;
/*分配*/
kset = kzalloc(sizeof(*kset), GFP_KERNEL);
if (!kset)
return NULL;
/*设置kset->kobj.name,创建目录sysfs_dirent结构时会用来设置s_name*/
retval = kobject_set_name(&kset->kobj, name);
if (retval) {
kfree(kset);
return NULL;
}
/*设置kset->uevent_ops*/
kset->uevent_ops = uevent_ops;
/*设置父kobj,为NULL*/
kset->kobj.parent = parent_kobj;
/*
* The kobject of this kset will have a type of kset_ktype and belong to
* no kset itself. That way we can properly free it when it is
* finished being used.
*/
/*设置kset->kobj.ktype,kset_ktype是全局变量,创建目录下的文件时会用到*/
kset->kobj.ktype = &kset_ktype;
/*设置kset->kobj.kset,为NULL*/
kset->kobj.kset = NULL;
return kset;
}
创建kset时需要用到的几个全局结构
static struct kset_uevent_ops bus_uevent_ops = {
.filter = bus_uevent_filter,
};
static struct kobj_type kset_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.release = kset_release,
};
struct sysfs_ops kobj_sysfs_ops = {
.show = kobj_attr_show,
.store = kobj_attr_store,
};
2.注册kset
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;
}
2.1初始化kset(创建kset时,已经初始化一部分)
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;
}
void kref_init(struct kref *kref)
{
kref_set(kref, 1);
}
2.2 根据kset->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);
/*如果kobj->kset不为空,则将kobj->entry链接到kobj->kset->list双向链表中
*如果parent为空,则将parent设置为kobj->kset->kobj。
*/
/* join kset if set, use it as parent if we do not already have one */
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;
/* be noisy on error issues */
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;
}
因为kobj->parent和kobj->kset此时都是NULL,所以该函数主要调用create_dir()创建目录,并将
kobj->state_in_sysfs设置为1。
static int create_dir(struct kobject *kobj)
{
int error = 0;
if (kobject_name(kobj)) {
error = sysfs_create_dir(kobj);
if (!error) {
error = populate_dir(kobj);
if (error)
sysfs_remove_dir(kobj);
}
}
return error;
}
调用sysfs_create_dir()创建目录,之后调用populate_dir()创建目录下的文件,populate_dir()会通过
kobj->ktype->default_attrs的值来判断是否需要创建文件。因为kobj->ktype->default_attrs代表着目
录下文件的信息。
先来分析sysfs_create_dir()
int sysfs_create_dir(struct kobject * kobj)
{
struct sysfs_dirent *parent_sd, *sd;
int error = 0;
BUG_ON(!kobj);
/*如果kobj->parent为空,表示直接在/sys下建立目录。
*sysfs_root是全局变量,在sysfs_init()函数中有介绍
*/
if (kobj->parent)
parent_sd = kobj->parent->sd;
else
parent_sd = &sysfs_root;
/*创建目录,sd指向代表所创建目录的sysfs_dirent结构*/
error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);
/*建立kobj和sysfs_dirent之间的关联*/
if (!error)
kobj->sd = sd;
return error;
}
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;
/* allocate */
/*分配的sysfs__dirent结构,并简单初始化*/
sd = sysfs_new_dirent(name, mode, SYSFS_DIR);
if (!sd)
return -ENOMEM;
/*建立sysfs_dirent和kobj之间的关联*/
sd->s_dir.kobj = kobj;
/* link in */
/*先保存父目录的sysfs_dirent和inode结构*/
sysfs_addrm_start(&acxt, parent_sd);
/**/
rc = sysfs_add_one(&acxt, sd);
/*更新父目录的时间戳*/
sysfs_addrm_finish(&acxt);
/*保存上面分配的sysfs_dirent结构*/
if (rc == 0)
*p_sd = sd;
else
sysfs_put(sd);
return rc;
}
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;
}
/*分配sysfs_dirent所需的内存*/
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);
/*设置name,mode,flags。name为kobj->name,也就是
*kset_create_and_add("bus",..)的第一个参数"bus"
*/
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;
}
void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
struct sysfs_dirent *parent_sd)
{
struct inode *inode;
memset(acxt, 0, sizeof(*acxt));
/*保存父目录的sysfs_dirent结构*/
acxt->parent_sd = parent_sd;
/* Lookup parent inode. inode initialization is protected by
* sysfs_mutex, so inode existence can be determined by
* looking up inode while holding sysfs_mutex.
*/
mutex_lock(&sysfs_mutex);
/*查找父目录的inode结构,若找到,则保存*/
inode = ilookup5(sysfs_sb, parent_sd->s_ino, sysfs_ilookup_test,
parent_sd);
if (inode) {
WARN_ON(inode->i_state & I_NEW);
/* parent inode available */
acxt->parent_inode = inode;
/* sysfs_mutex is below i_mutex in lock hierarchy.
* First, trylock i_mutex. If fails, unlock
* sysfs_mutex and lock them in order.
*/
if (!mutex_trylock(&inode->i_mutex)) {
mutex_unlock(&sysfs_mutex);
mutex_lock(&inode->i_mutex);
mutex_lock(&sysfs_mutex);
}
}
}
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;
/*设置和父目录sysfs_dirent之间的关联*/
sd->s_parent = sysfs_get(acxt->parent_sd);
/*增加父目录的索引节点的inode->i_nlink值*/
if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode)
inc_nlink(acxt->parent_inode);
acxt->cnt++;
/*将sysfs_dirent链接到兄弟sysfs_dirent中*/
sysfs_link_sibling(sd);
return 0;
}
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);
/* Store directory entries in order by ino. This allows
* readdir to properly restart without having to add a
* cursor into the s_dir.children list.
*/
/*按s_ino的大小顺序链接*/
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;
}
void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
{
/* release resources acquired by sysfs_addrm_start() */
mutex_unlock(&sysfs_mutex);
/*更新时间戳*/
if (acxt->parent_inode) {
struct inode *inode = acxt->parent_inode;
/* if added/removed, update timestamps on the parent */
if (acxt->cnt)
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
mutex_unlock(&inode->i_mutex);
iput(inode);
}
/* kill removed sysfs_dirents */
/*如果是删除目录,则执行删除目录的操作*/
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);
}
}
还剩下populate_dir()函数还没分析
static int populate_dir(struct kobject *kobj)
{
struct kobj_type *t = get_ktype(kobj);
struct attribute *attr;
int error = 0;
int i;
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;
}
因为kobj->ktype在kset_create()中被设置为全局变量kset_ktype,所以t->default_attrs为NULL,
该函数此时也为空。以后会遇到不为空的情况,到时再分析。
通过以上分析,可以认为创建目录的实质是创建一个sysfs_dirent结构,并建立和父目录,兄弟目录,子文件以及对应kobj之间的关联。
2.3
kobject_uevent(&k->kobj, KOBJ_ADD)和热拔插有关,估计是通知应用层有KOBJ_ADD事件发生吧,以后再分析。
最后给出数据框图,目录建立部分就不画出了。