總線初始化及/sys下bus目錄的建立

    設備模型中,關心總線,設備,驅動這三個實體,總線將設備和驅動綁定,在系統每註冊一個設備的時候,會尋找與之匹配的驅動。
相反,在系統每註冊一個驅動的時候,尋找與之匹配的設備,匹配是由總線來完成的。本文分析總線的初始化,即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事件發生吧,以後再分析。

最後給出數據框圖,目錄建立部分就不畫出了。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章