kobject创建目录。的本质是通过sysfs创建的。通过kernfs_node *ns来建立关系。
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;
}