总线初始化及/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事件发生吧,以后再分析。

最后给出数据框图,目录建立部分就不画出了。



  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值