相反,在系統每註冊一個驅動的時候,尋找與之匹配的設備,匹配是由總線來完成的。本文分析總線的初始化,即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.註冊ksetint 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事件發生吧,以後再分析。
最後給出數據框圖,目錄建立部分就不畫出了。