kobject创建目录。的本质是通过sysfs创建的。通过kernfs_node *ns来建立关系。

kobject创建文件夹和文件测试。_算法

kobject_create_and_add 

最简单方式创建kobject。内部创建对象。参数为名字,如果parent为空,则创建在/sys/目录下。否则为parent的子目录

struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
	struct kobject *kobj;
	int retval;

	kobj = kobject_create(); //kobject_init
	if (!kobj)
		return NULL;

	retval = kobject_add(kobj, parent, "%s", name);
	if (retval) {
		pr_warn("%s: kobject_add error: %d\n", __func__, retval);
		kobject_put(kobj);
		kobj = NULL;
	}
	return kobj;
}

kobject_create:内部自动关联dynamic_kobj_type,有代码可以看出,sysfs_ops的show/store函数会将动态将attribute转换成kobj_attribute。并找到show/store方法运行,方便调用用户的函数。

struct sysfs_ops {
	ssize_t	(*show)(struct kobject *, struct attribute *, char *);
	ssize_t	(*store)(struct kobject *, struct attribute *, const char *, size_t);
};

 继续

struct kobject *kobject_create(void)
{
	struct kobject *kobj;

	kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
	if (!kobj)
		return NULL;

	kobject_init(kobj, &dynamic_kobj_ktype);
	return kobj;
}


static struct kobj_type dynamic_kobj_ktype = {
	.release	= dynamic_kobj_release,
	.sysfs_ops	= &kobj_sysfs_ops,
};

 继续

结构体:attr后面是show/store函数指针
struct kobj_attribute {
        struct attribute attr;
        ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
                        char *buf);
        ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
                         const char *buf, size_t count);
};


/* default kobject attribute operations */
static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
			      char *buf)
{
	struct kobj_attribute *kattr;
	ssize_t ret = -EIO;

	kattr = container_of(attr, struct kobj_attribute, attr);
	if (kattr->show)
		ret = kattr->show(kobj, kattr, buf);
	return ret;
}

static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
			       const char *buf, size_t count)
{
	struct kobj_attribute *kattr;
	ssize_t ret = -EIO;

	kattr = container_of(attr, struct kobj_attribute, attr);
	if (kattr->store)
		ret = kattr->store(kobj, kattr, buf, count);
	return ret;
}

const struct sysfs_ops kobj_sysfs_ops = {
	.show	= kobj_attr_show,
	.store	= kobj_attr_store,
};

kobject_init_and_add

方式二:自己创建kobject和kobj_type 等。这种方式给用户自由,比如用户自己定义了attribute结果。那么如果用户需要自定义类似kobj_attribute的结构体就需要自己实现sysfs_os中的show/store函数。

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;
}

例子:

#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("colorfulshark@hotmail.com");

static struct kobject mykobj;

//自己创建的show函数,简单实使用没有再调用自己的类attri后面的其他方法。
static ssize_t fw_cfg_sysfs_attr_show(struct kobject *kobj, struct attribute *a,
		                                      char *buf)
{
	dump_stack();
	return sprintf(buf, "fw_cfg_sysfs_attr_show\n");
}


//自己创建的sysfs_ops
static const struct sysfs_ops fw_cfg_sysfs_attr_ops = {
	        .show = fw_cfg_sysfs_attr_show,
};

//自创建的kobj_type
static struct kobj_type fw_cfg_sysfs_entry_ktype = {
	       // .default_attrs = fw_cfg_sysfs_entry_attrs,
	        .sysfs_ops = &fw_cfg_sysfs_attr_ops,
	        //.release = fw_cfg_sysfs_release_entry,
};


static struct kobject *fw_cfg_top_ko;

static ssize_t fw_cfg_showrev(struct kobject *k, struct attribute *a, char *buf)
{
	dump_stack();
	        return sprintf(buf, "hello jaon\n");
}


static const struct {
	        struct attribute attr;
		        ssize_t (*show)(struct kobject *k, struct attribute *a, char *buf);
} fw_cfg_rev_attr = {
	        .attr = { .name = "rev", .mode = S_IRUSR },
		        .show = fw_cfg_showrev,
};
static int xxx_init(void)
{      
       dump_stack();	
	    
       fw_cfg_top_ko = kobject_create_and_add("test1", NULL);
	sysfs_create_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr);       
      
	kobject_init_and_add(&mykobj, &fw_cfg_sysfs_entry_ktype,
			                                   NULL, "subxx1");
	sysfs_create_file(&mykobj,&fw_cfg_rev_attr.attr); //这里我复用了fw_cfg_rev_attr.attr 这个attribute。当然也可以自定义一个,在show中用container_of 找到自己定义的show函数指针。

       printk(KERN_ALERT "module xxx init\n");
            return 0;
}       

static void xxx_exit(void)
{       
	    printk(KERN_ALERT "module xxx exit\n");
}       
module_init(xxx_init);
module_exit(xxx_exit);

方式1:

[root@localhost ~]# cat /sys/kernel/kexec_crash_size 
[ 1406.506644] CPU: 4 PID: 1218 Comm: cat Not tainted 4.19.0-fix-full-10+ #18
[ 1406.517365] Hardware name: PHYTIUM LTD D2000/D2000, BIOS  
[ 1406.526003] Call trace:
[ 1406.531549]  dump_backtrace+0x0/0x1b8
[ 1406.538233]  show_stack+0x24/0x30
[ 1406.544511]  dump_stack+0x90/0xb4
[ 1406.550767]  kexec_crash_size_show+0x1c/0x48
[ 1406.557975]  kobj_attr_show+0x14/0x28
[ 1406.564573]  sysfs_kf_seq_show+0x9c/0x138
[ 1406.571518]  kernfs_seq_show+0x44/0x50
[ 1406.578210]  seq_read+0xd4/0x4a8
[ 1406.584379]  kernfs_fop_read+0x16c/0x218
[ 1406.591254]  __vfs_read+0x60/0x188
[ 1406.597612]  vfs_read+0x94/0x150
[ 1406.603770]  ksys_read+0x6c/0xd8
[ 1406.609957]  __arm64_sys_read+0x24/0x30
[ 1406.616751]  el0_svc_handler+0x84/0x140
[ 1406.623552]  el0_svc+0x8/0xc

方式2:

[217963.967082]  show_stack+0x52/0x58
[217963.967086]  dump_stack_lvl+0x4a/0x5f
[217963.967089]  dump_stack+0x10/0x12
[217963.967093]  fw_cfg_sysfs_attr_show+0x13/0x2b [test]  //这里直接调用
[217963.967095]  sysfs_kf_seq_show+0xa1/0x100

[217963.967098]  kernfs_seq_show+0x27/0x30
[217963.967100]  seq_read_iter+0x120/0x4b0
[217963.967117]  ? do_anonymous_page+0x1f5/0x3b0
[217963.967119]  kernfs_fop_read_iter+0x2c/0x30
[217963.967121]  new_sync_read+0x110/0x190
[217963.967124]  vfs_read+0xff/0x1a0
[217963.967126]  ksys_read+0x67/0xe0
[217963.967128]  __x64_sys_read+0x19/0x20
[217963.967129]  do_syscall_64+0x5c/0xc0
[217963.967130]  ? exit_to_user_mode_prepare+0x37/0xb0
[217963.967133]  ? irqentry_exit_to_user_mode+0x9/0x20
[217963.967135]  ? irqentry_exit+0x19/0x30
[217963.967136]  ? exc_page_fault+0x89/0x160
[217963.967138]  ? asm_exc_page_fault+0x8/0x30
[217963.967623]  entry_SYSCALL_64_after_hwframe+0x44/0xae
 

kset_create_and_add

kset结构体:包含一个kobject,也会创建目录,将相同类型的kobject进行关联。

//struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.
struct kset {
	struct list_head list;  //链表,
	spinlock_t list_lock;
	struct kobject kobj; //包含一个完整kobject
	const struct kset_uevent_ops *uevent_ops;
} __randomize_layout;
struct kset *kset_create_and_add(const char *name,
				 const 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;
}


static struct kset *kset_create(const char *name,
				const 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;
	retval = kobject_set_name(&kset->kobj, "%s", name);
	if (retval) {
		kfree(kset);
		return NULL;
	}
	kset->uevent_ops = uevent_ops;
	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.kset = NULL;

	return kset;
}

如果自己创建的kobject中的kset非NULL,那么在初始化接口kobject_init_and_add->kobject_add_internal:

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);

	/* 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);  //加入kset的链表
		kobj->parent = parent;
	}

................
}


static void kobj_kset_leave(struct kobject *kobj)
{
	if (!kobj->kset)
		return;

	spin_lock(&kobj->kset->list_lock);
	list_del_init(&kobj->entry);
	spin_unlock(&kobj->kset->list_lock);
	kset_put(kobj->kset);
}

后续通过kset_find_obj函数查找:

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_unless_zero(k);
			break;
		}
	}

	spin_unlock(&kset->list_lock);
	return ret;
}

创建文件目录和文件(attribute)的本质:

kobject_add_internal
	create_dir(kobj)
		sysfs_create_dir_ns(kobj, kobject_namespace(kobj));   //fs/sysfs/dir.c 
			 kernfs_create_dir_ns              //fs/kernfs/dir.c 创建 kernfs_node * kn目录,union配置为kernfs_elem_dir
				 kernfs_new_node    
				 kernfs_add_one 
            设置kobject->sd为kn 
		populate_dir(kobj);  //为默认的default_attrs[]创建文件属性
			sysfs_create_file(kobj, attr); //循环调用
				sysfs_create_file_ns(kobj, attr, NULL);
					sysfs_add_file_mode_ns    //fs/sysfs/file.c
						 __kernfs_create_file  //fs/kernfs/file.c  设置union的kernfs_elem_attr
							 kernfs_new_node //创建kernfs_node *kn ,以kobject->sd为parent
							 kernfs_add_one

amdgpu驱动中在/sys/bus/pci/devices/0000:0f:00.0/下面创建vram相关的属性文件:

root@wzm-phytium-d2000:/sys/bus/pci/devices/0000:0f:00.0# ls mem_info_*
mem_info_gtt_total  mem_info_preempt_used    mem_info_vis_vram_used  mem_info_vram_used
mem_info_gtt_used   mem_info_vis_vram_total  mem_info_vram_total     mem_info_vram_vendor

drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c 

/**
 * DOC: mem_info_vram_total
 *
 * The amdgpu driver provides a sysfs API for reporting current total VRAM
 * available on the device
 * The file mem_info_vram_total is used for this and returns the total
 * amount of VRAM in bytes
 */
static ssize_t amdgpu_mem_info_vram_total_show(struct device *dev,
                struct device_attribute *attr, char *buf)
{
        struct drm_device *ddev = dev_get_drvdata(dev);
        struct amdgpu_device *adev = drm_to_adev(ddev);

        return snprintf(buf, PAGE_SIZE, "%llu\n", adev->gmc.real_vram_size);
}

/**
 * DOC: mem_info_vis_vram_total
 *
 * The amdgpu driver provides a sysfs API for reporting current total
 * visible VRAM available on the device
 * The file mem_info_vis_vram_total is used for this and returns the total
 * amount of visible VRAM in bytes
 */
static ssize_t amdgpu_mem_info_vis_vram_total_show(struct device *dev,
                struct device_attribute *attr, char *buf)
{
        struct drm_device *ddev = dev_get_drvdata(dev);
        struct amdgpu_device *adev = drm_to_adev(ddev);

        return snprintf(buf, PAGE_SIZE, "%llu\n", adev->gmc.visible_vram_size);
}




static DEVICE_ATTR(mem_info_vram_total, S_IRUGO,
                   amdgpu_mem_info_vram_total_show, NULL);
static DEVICE_ATTR(mem_info_vis_vram_total, S_IRUGO,
                   amdgpu_mem_info_vis_vram_total_show,NULL);
static DEVICE_ATTR(mem_info_vram_used, S_IRUGO,
                   amdgpu_mem_info_vram_used_show, NULL);
static DEVICE_ATTR(mem_info_vis_vram_used, S_IRUGO,
                   amdgpu_mem_info_vis_vram_used_show, NULL);
static DEVICE_ATTR(mem_info_vram_vendor, S_IRUGO,
                   amdgpu_mem_info_vram_vendor, NULL);

static const struct attribute *amdgpu_vram_mgr_attributes[] = {
        &dev_attr_mem_info_vram_total.attr,
        &dev_attr_mem_info_vis_vram_total.attr,
        &dev_attr_mem_info_vram_used.attr,
        &dev_attr_mem_info_vis_vram_used.attr,
        &dev_attr_mem_info_vram_vendor.attr,
        NULL
};

int amdgpu_vram_mgr_init(struct amdgpu_device *adev)
{
        struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr;
        struct ttm_resource_manager *man = &mgr->manager;
        int ret;

        ttm_resource_manager_init(man, adev->gmc.real_vram_size >> PAGE_SHIFT);

        man->func = &amdgpu_vram_mgr_func;

        drm_mm_init(&mgr->mm, 0, man->size);
        spin_lock_init(&mgr->lock);

        /* Add the two VRAM-related sysfs files */
        ret = sysfs_create_files(&adev->dev->kobj, amdgpu_vram_mgr_attributes);
        if (ret)
                DRM_ERROR("Failed to register sysfs\n");

        ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, &mgr->manager);
        ttm_resource_manager_set_used(man, true);
        return 0;
}