从inode到kobject

写的很乱,还需要整理思路。 这几天看的文件系统,先从底下的内核对象kobject,kset看起的,这是所有device对应的linux文件。了解到这是sysfs文件系统的东西,而sysfs文件系统不会一开始就挂上,在系统调用开始最先经过的是vfs,所以看到了vfsmount部分,那里讲述了vfs文件系统以及其他系统的挂载,而这一切都有一个inode的结构做桥梁,所有文件都离不开inode,在inode之下是dentry的概念,inode代表了实际物理意义的文件,dentry代表的目录项。在inode之上是super_block,每一个文件系统对应一个super_block实例,mount一个文件系统,需要分注册和装载两步,实际是申请一个super_block,然后填充。那么sysfs作为一个在vfs之下的fs,是如何挂载的,这个可以参考vfs挂载子系统来看以及sys_mount还有本篇开头部分。在sysfs中又是如何装载系统的这是本文前面所讲的,也涉及到了inode和kobject的联系,学习inode时有一个dentry结构,有个dentry->d_fsdata成员指向了sysfs下的sysfs_dirent结构。还有dirent结构中的属性集,symlink, bin, dir等,这些sysfs中的文件在内核执行添加内核对象时是如何设置的,它经过各种属性,如:attribute,  kobj_attribute, attribte_group,sysfs_ops, kobj_type等一切联系到一起。这些东西还是要配合图和实操会更好,后面还是要从新理一遍加深理解。看完这些内容,起码知道了挂载系统,挂载总线,挂载设备的原理,对内核的分层思想也有所体会,虽然整篇文章我都没有特别留意结构体中出现的list_head链表,但是它每出现一次就基本分了个层,环环相扣,还有到底什么是文件,文件也只是数据而已,只有用ls命令时,才会解析出来东西。

 

kobject是内核对象(sysfs子系统), inode是物理意义上的文件,他俩之间通过何种方式取得联系?(这个问题我没有做最后总结,通篇文章给出了所有过程要调用的函数以及对应的结构体,它们确实是相关的)

装载sysfs文件系统,mount系统调用最后将填充超级块的工作委托给sysfs_fill_super。先了解基本的数据结构如何相关联。

值得记忆的是:要使一个对象在sysfs文件系统中可见,需要调用kobject_add。但如果kobject是某个

内核子系统的成员,那么向 sysfs 的注册是自动进行的。

0,装载文件系统

 

在kobject中有:

struct kobject{
...
    struct sysfs_dirent	*sd;
...
};

sys_dirent是sysfs的主要数据结构,表示sysfs的目录项。每个sysfs结点都表示为sysfs_dirent的一个实例。

两者之间的联系通过dentry->d_fsdata建立。该成员指向与dentry实例相关的sysfs_dirent实例。

sysfs_dirent结构:

struct sysfs_dirent {
	atomic_t		s_count;
	atomic_t		s_active;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map	dep_map;
#endif
	struct sysfs_dirent	*s_parent;
	const char		*s_name;

	struct rb_node		s_rb;

	union {
		struct completion	*completion;
		struct sysfs_dirent	*removed_list;
	} u;

	const void		*s_ns; /* namespace tag */
	unsigned int		s_hash; /* ns + name hash */
	union {匿名联合, 根据数据项类型来匹配设置
		struct sysfs_elem_dir		s_dir;
		struct sysfs_elem_symlink	s_symlink;
		struct sysfs_elem_attr		s_attr;
		struct sysfs_elem_bin_attr	s_bin_attr;
	};

	unsigned short		s_flags;
	umode_t 		s_mode;
	unsigned int		s_ino;  //子节点链表中的所有节点按照s_ino以递减次序排列
	struct sysfs_inode_attrs *s_iattr;
};

根据sysfs数据项的类型不同,与之关联的数据类型也不同。一个数据项一次只能表示一种类型。

sysfs数据项也由struct dentry实例表示。

sysfs_get_inode用于创建一个新的struct inode实例,作为整个sysfs树的起点。不仅用于获得sysfs根目录inode,它也是一个通用函数,可用于任何sysfs数据项。

struct inode * sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd)
{
	struct inode *inode;

	inode = iget_locked(sb, sd->s_ino);
	if (inode && (inode->i_state & I_NEW))
		sysfs_init_inode(sd, inode);

	return inode;
}

sysfs_init_inode从头开始构造一个新的inode实例,整个函数与上一个函数应用的场景不同,如果inode已经存在,则上边的函数会检查失败退出,取而代之的是用sysfs_init_inode函数。

static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
{
	struct bin_attribute *bin_attr;

//设置inode操作,地址空间操作,文件操作
	inode->i_private = sysfs_get(sd);
	inode->i_mapping->a_ops = &sysfs_aops;
	inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
	inode->i_op = &sysfs_inode_operations;

	set_default_inode_attr(inode, sd->s_mode); //设置默认属性gid, uid等等
	sysfs_refresh_inode(sd, inode);

    //根据类型初始化成员, 软链接、目录、属性、bin属性
	/* initialize inode according to type */
	switch (sysfs_type(sd)) {
	case SYSFS_DIR:
		inode->i_op = &sysfs_dir_inode_operations;
		inode->i_fop = &sysfs_dir_operations;
		break;
	case SYSFS_KOBJ_ATTR:
		inode->i_size = PAGE_SIZE;
		inode->i_fop = &sysfs_file_operations;
		break;
	case SYSFS_KOBJ_BIN_ATTR:
		bin_attr = sd->s_bin_attr.bin_attr;
		inode->i_size = bin_attr->size;
		inode->i_fop = &bin_fops;
		break;
	case SYSFS_KOBJ_LINK:
		inode->i_op = &sysfs_symlink_inode_operations;
		break;
	default:
		BUG();
	}

	unlock_new_inode(inode);
}

函数最终在sysfs_fill_super调用来填充一个超级块,d_make_root为根目录分配一个dentry实例之后,将建立sysfs数据和文件系统项之间的关联。

static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
{
	struct inode *inode;
	struct dentry *root;

	sb->s_blocksize = PAGE_CACHE_SIZE;
	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
	sb->s_magic = SYSFS_MAGIC;
	sb->s_op = &sysfs_ops;
	sb->s_time_gran = 1;

	/* get root inode, initialize and unlock it */
	mutex_lock(&sysfs_mutex);
	inode = sysfs_get_inode(sb, &sysfs_root);
	mutex_unlock(&sysfs_mutex);
	if (!inode) {
		pr_debug("sysfs: could not get root inode\n");
		return -ENOMEM;
	}

	/* instantiate and link root dentry */
	root = d_make_root(inode);
	if (!root) {
		pr_debug("%s: could not get root dentry!\n",__func__);
		return -ENOMEM;
	}
	
	
	root->d_fsdata = &sysfs_root; 
//由文件系统内部使用,这样sysfs能够在sysfs_dirent和dentry实例之间建立一个关联。
//sysfs_root是一个静态的struct sysfs_dirent实例,表示根目录对应的数据项。
//d_fsdata总是指向相关的struct sysfs_dirent实例。
//在sysfs中,不仅根数据项如此,
//所有其他的数据项也是一样。这种关联使得内核能够从通用的VFS数据结构得到特定于sysfs的数据
	sb->s_root = root;
	return 0;
}


struct sysfs_dirent sysfs_root = {
    .s_name = "",
    .s_count = ATOMIC_INIT(1),
    .s_flags = SYSFS_DIR,
    .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
    .s_ino = 1,
};

 

struct dentry *d_make_root(struct inode *root_inode)
{
	struct dentry *res = NULL;

	if (root_inode) {
		static const struct qstr name = { .name = "/", .len = 1 };

		res = __d_alloc(root_inode->i_sb, &name);
		if (res)
			d_instantiate(res, root_inode);
		else
			iput(root_inode);
	}
	return res;
}

 

 

2,向sysfs添加内容

注册子系统:

属性,特定于子系统的底层工作

edac_device_sysfs.c       设备子系统属性

struct ctl_info_attribute {
	struct attribute attr;
	ssize_t(*show) (struct edac_device_ctl_info *, char *);
	ssize_t(*store) (struct edac_device_ctl_info *, const char *, size_t);
};

 

如何将默认属性集关联到该子系统的所有kobject,为此,需要使用一个kobj_type,举例子:


static const struct sysfs_ops device_ctl_info_ops = {
	.show = edac_dev_ctl_info_show,
	.store = edac_dev_ctl_info_store
};


//属性集
#define CTL_INFO_ATTR(_name,_mode,_show,_store)        \
static struct ctl_info_attribute attr_ctl_info_##_name = {      \
	.attr = {.name = __stringify(_name), .mode = _mode },   \
	.show   = _show,                                        \
	.store  = _store,                                       \
};

CTL_INFO_ATTR(log_ue, S_IRUGO | S_IWUSR,
	edac_device_ctl_log_ue_show, edac_device_ctl_log_ue_store);
CTL_INFO_ATTR(log_ce, S_IRUGO | S_IWUSR,
	edac_device_ctl_log_ce_show, edac_device_ctl_log_ce_store);
CTL_INFO_ATTR(panic_on_ue, S_IRUGO | S_IWUSR,
	edac_device_ctl_panic_on_ue_show,
	edac_device_ctl_panic_on_ue_store);
CTL_INFO_ATTR(poll_msec, S_IRUGO | S_IWUSR,
	edac_device_ctl_poll_msec_show, edac_device_ctl_poll_msec_store);

/* Base Attributes of the EDAC_DEVICE ECC object */
static struct ctl_info_attribute *device_ctrl_attr[] = {
	&attr_ctl_info_panic_on_ue,
	&attr_ctl_info_log_ue,
	&attr_ctl_info_log_ce,
	&attr_ctl_info_poll_msec,
	NULL,
};

static struct kobj_type ktype_device_ctrl = {
	.release = edac_device_ctrl_master_release,
	.sysfs_ops = &device_ctl_info_ops,
	.default_attrs = (struct attribute **)device_ctrl_attr,
};

在edac_device_register_sysfs_main_kobj()   注册子系统main函数中主要是调用三个函数,先看一下源码:

int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
{
	struct bus_type *edac_subsys;
	int err;

	debugf1("%s()\n", __func__);

	/* get the /sys/devices/system/edac reference */
	edac_subsys = edac_get_sysfs_subsys();
	if (edac_subsys == NULL) {
		debugf1("%s() no edac_subsys error\n", __func__);
		err = -ENODEV;
		goto err_out;
	}

	/* Point to the 'edac_subsys' this instance 'reports' to */
	edac_dev->edac_subsys = edac_subsys;

	/* Init the devices's kobject */
	memset(&edac_dev->kobj, 0, sizeof(struct kobject));

	/* Record which module 'owns' this control structure
	 * and bump the ref count of the module
	 */
	edac_dev->owner = THIS_MODULE;

	if (!try_module_get(edac_dev->owner)) {
		err = -ENODEV;
		goto err_mod_get;
	}

	/* register */
	err = kobject_init_and_add(&edac_dev->kobj, &ktype_device_ctrl,
				   &edac_subsys->dev_root->kobj,
				   "%s", edac_dev->name);
	if (err) {
		debugf1("%s()Failed to register '.../edac/%s'\n",
			__func__, edac_dev->name);
		goto err_kobj_reg;
	}
	kobject_uevent(&edac_dev->kobj, KOBJ_ADD);

	/* At this point, to 'free' the control struct,
	 * edac_device_unregister_sysfs_main_kobj() must be used
	 */

	debugf4("%s() Registered '.../edac/%s' kobject\n",
		__func__, edac_dev->name);

	return 0;

	/* Error exit stack */
err_kobj_reg:
	module_put(edac_dev->owner);

err_mod_get:
	edac_put_sysfs_subsys();

err_out:
	return err;
}

目的是创建一个对应于kobj_type的kset的过程,在上面的函数中最终调用以下三个函数,注册了总线。

edac_get_sysfs_subsys()调用的subsys_system_register();

kobject_init_and_add();

kobject_uevent();

可以看到kset是从最开始调用的函数一层一层往下,越底层的目录越高,这不难理解,因为要先创建/sys/device/,才能在这个目录的基础上创建/sys/device/system,device_initialize的作用就是初始化设备,来准备设备供其他层使用。然后通过第二个函数device_add把设备添加到kobject的层次结构中,再将其添加到驱动程序模型的其它相关子系统中。在device_add函数中调用了kobject_add实现添加到kobject结构的功能。最后device_create_file进行创建文件,在sysfs中创建文件最终都会调用sysfs_create_file来执行。kobject_set_name和set_dev_name的函数成分一样,一样的函数体,两个名称而已,实现方式是用va_start的方式,在最终ls时通过系统调用打印出来具体的名称。

1,subsys_system_register()负责子系统的注册过程,所有“system”子系统都有一个/sys/devices/system/<name>根设备和子系统的名称。根设备可以携带子系统范围的属性。所有注册的设备都位于这个根设备的下面,并以附加了简单枚举号的子系统命名。注册的设备没有明确的名称;

1.1 bus_register, 总线注册,负责向kobject基础注册总线,设置名称,然后再注册这个总线拥有子系统。kset是目录,kobject是对应的设备文件。里面调用了kset_create_and_add创建了两个目录。

int __bus_register(struct bus_type *bus, struct lock_class_key *key)
{
	int retval;
	struct subsys_private *priv;

	priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	priv->bus = bus;
	bus->p = priv;

	BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

	retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
	if (retval)
		goto out;

	priv->subsys.kobj.kset = bus_kset;
	priv->subsys.kobj.ktype = &bus_ktype;
	priv->drivers_autoprobe = 1;

	retval = kset_register(&priv->subsys);
	if (retval)
		goto out;

	retval = bus_create_file(bus, &bus_attr_uevent);
	if (retval)
		goto bus_uevent_fail;

	priv->devices_kset = kset_create_and_add("devices", NULL,
						 &priv->subsys.kobj);
	if (!priv->devices_kset) {
		retval = -ENOMEM;
		goto bus_devices_fail;
	}

	priv->drivers_kset = kset_create_and_add("drivers", NULL,
						 &priv->subsys.kobj);
	if (!priv->drivers_kset) {
		retval = -ENOMEM;
		goto bus_drivers_fail;
	}

	INIT_LIST_HEAD(&priv->interfaces);
	__mutex_init(&priv->mutex, "subsys mutex", key);
	klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
	klist_init(&priv->klist_drivers, NULL, NULL);

	retval = add_probe_files(bus);
	if (retval)
		goto bus_probe_files_fail;

	retval = bus_add_attrs(bus);
	if (retval)
		goto bus_attrs_fail;

	pr_debug("bus: '%s': registered\n", bus->name);
	return 0;

bus_attrs_fail:
	remove_probe_files(bus);
bus_probe_files_fail:
	kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
	kset_unregister(bus->p->devices_kset);
bus_devices_fail:
	bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
	kset_unregister(&bus->p->subsys);
out:
	kfree(bus->p);
	bus->p = NULL;
	return retval;
}
EXPORT_SYMBOL_GPL(__bus_register);

1.2 device_register,设备注册,函数体里只有两行,分两步完成注册过程,初始化设备and添加设备。

1.2.1 device_initialize()上面图片已经包含了函数体,为了方便阅读,再贴一遍:

void device_initialize(struct device *dev)
{
	dev->kobj.kset = devices_kset;
	kobject_init(&dev->kobj, &device_ktype);
	INIT_LIST_HEAD(&dev->dma_pools);
	mutex_init(&dev->mutex);
	lockdep_set_novalidate_class(&dev->mutex);
	spin_lock_init(&dev->devres_lock);
	INIT_LIST_HEAD(&dev->devres_head);
	device_pm_init(dev);
	set_dev_node(dev, -1);
}

 device_kset是一个全局定义的kset类型的指针,也就是一个设备目录。device_ktype是一个kobj_type的实例。

函数传入一个实参device,把device中的kobject按照提前设置好的kobj_type类型进行初始化。

1.2.2 device_add()添加设备,创建文件,并申请主设备号。

int device_add(struct device *dev)
{
	struct device *parent = NULL;
	struct kobject *kobj;
	struct class_interface *class_intf;
	int error = -EINVAL;

	dev = get_device(dev);
	if (!dev)
		goto done;

	if (!dev->p) {
		error = device_private_init(dev);
		if (error)
			goto done;
	}

	/*
	 * for statically allocated devices, which should all be converted
	 * some day, we need to initialize the name. We prevent reading back
	 * the name, and force the use of dev_name()
	 */
	if (dev->init_name) {
		dev_set_name(dev, "%s", dev->init_name);
		dev->init_name = NULL;
	}

	/* subsystems can specify simple device enumeration */
	if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
		dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);

	if (!dev_name(dev)) {
		error = -EINVAL;
		goto name_error;
	}

	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);

	parent = get_device(dev->parent);
	kobj = get_device_parent(dev, parent);
	if (kobj)
		dev->kobj.parent = kobj;

	/* use parent numa_node */
	if (parent)
		set_dev_node(dev, dev_to_node(parent));

	/* first, register with generic layer. */
	/* we require the name to be set before, and pass NULL */
	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
	if (error)
		goto Error;

	/* notify platform of device entry */
	if (platform_notify)
		platform_notify(dev);

	error = device_create_file(dev, &uevent_attr);
	if (error)
		goto attrError;

	if (MAJOR(dev->devt)) {
		error = device_create_file(dev, &devt_attr);
		if (error)
			goto ueventattrError;

		error = device_create_sys_dev_entry(dev);
		if (error)
			goto devtattrError;

		devtmpfs_create_node(dev);
	}

	error = device_add_class_symlinks(dev);
	if (error)
		goto SymlinkError;
	error = device_add_attrs(dev);
	if (error)
		goto AttrsError;
	error = bus_add_device(dev);
	if (error)
		goto BusError;
	error = dpm_sysfs_add(dev);
	if (error)
		goto DPMError;
	device_pm_add(dev);

	/* Notify clients of device addition.  This call must come
	 * after dpm_sysfs_add() and before kobject_uevent().
	 */
	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_ADD_DEVICE, dev);

	kobject_uevent(&dev->kobj, KOBJ_ADD);
	bus_probe_device(dev);
	if (parent)
		klist_add_tail(&dev->p->knode_parent,
			       &parent->p->klist_children);

	if (dev->class) {
		mutex_lock(&dev->class->p->mutex);
		/* tie the class to the device */
		klist_add_tail(&dev->knode_class,
			       &dev->class->p->klist_devices);

		/* notify any interfaces that the device is here */
		list_for_each_entry(class_intf,
				    &dev->class->p->interfaces, node)
			if (class_intf->add_dev)
				class_intf->add_dev(dev, class_intf);
		mutex_unlock(&dev->class->p->mutex);
	}
done:
	put_device(dev);
	return error;
 DPMError:
	bus_remove_device(dev);
 BusError:
	device_remove_attrs(dev);
 AttrsError:
	device_remove_class_symlinks(dev);
 SymlinkError:
	if (MAJOR(dev->devt))
		devtmpfs_delete_node(dev);
	if (MAJOR(dev->devt))
		device_remove_sys_dev_entry(dev);
 devtattrError:
	if (MAJOR(dev->devt))
		device_remove_file(dev, &devt_attr);
 ueventattrError:
	device_remove_file(dev, &uevent_attr);
 attrError:
	kobject_uevent(&dev->kobj, KOBJ_REMOVE);
	kobject_del(&dev->kobj);
 Error:
	cleanup_device_parent(dev);
	if (parent)
		put_device(parent);
name_error:
	kfree(dev->p);
	dev->p = NULL;
	goto done;
}

 

注册设备需要调3个函数: kobject_init_and_add(),  kset_create_and_add();  其中kset_create_and_add调用了kset_register(),该函数最终调用了kobject_add_init(),kobject_add_internal(),和上述的kobject_uevent()。还有一个sysfs_create_group()。

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

 

注册一个sysfs子系统的步骤就是, 先设置属性集,为了相关联到kobject,需要用一个kobj_type的类型进行封装,然后设置一个对应当前kobj_type的kset, kset是一个容器类,关于kset和kobject的关系:kset,kobject分析





自己添加读写属性接口

在sysfs接口框架,按照这种模式可以为自己的驱动在sysfs中添加属性读写接口。

/*
 * Interface for Dynamic Logical Partitioning of I/O Slots on
 * RPA-compliant PPC64 platform.
 *
 * John Rose <johnrose@austin.ibm.com>
 * October 2003
 *
 * Copyright (C) 2003 IBM.
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 */
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include "rpadlpar.h"
#include "../pci.h"

#define DLPAR_KOBJ_NAME       "control"

/* Those two have no quotes because they are passed to __ATTR() which
 * stringifies the argument (yuck !)
 */
#define ADD_SLOT_ATTR_NAME    add_slot
#define REMOVE_SLOT_ATTR_NAME remove_slot

#define MAX_DRC_NAME_LEN 64

static ssize_t add_slot_store(struct kobject *kobj, struct kobj_attribute *attr,
			      const char *buf, size_t nbytes)
{
	char drc_name[MAX_DRC_NAME_LEN];
	char *end;
	int rc;

	if (nbytes >= MAX_DRC_NAME_LEN)
		return 0;

	memcpy(drc_name, buf, nbytes);

	end = strchr(drc_name, '\n');
	if (!end)
		end = &drc_name[nbytes];
	*end = '\0';

	rc = dlpar_add_slot(drc_name);
	if (rc)
		return rc;

	return nbytes;
}

static ssize_t add_slot_show(struct kobject *kobj,
			     struct kobj_attribute *attr, char *buf)
{
	return sprintf(buf, "0\n");
}

static ssize_t remove_slot_store(struct kobject *kobj,
				 struct kobj_attribute *attr,
				 const char *buf, size_t nbytes)
{
	char drc_name[MAX_DRC_NAME_LEN];
	int rc;
	char *end;

	if (nbytes >= MAX_DRC_NAME_LEN)
		return 0;

	memcpy(drc_name, buf, nbytes);

	end = strchr(drc_name, '\n');
	if (!end)
		end = &drc_name[nbytes];
	*end = '\0';

	rc = dlpar_remove_slot(drc_name);
	if (rc)
		return rc;

	return nbytes;
}

static ssize_t remove_slot_show(struct kobject *kobj,
				struct kobj_attribute *attr, char *buf)
{
	return sprintf(buf, "0\n");
}

static struct kobj_attribute add_slot_attr =
	__ATTR(ADD_SLOT_ATTR_NAME, 0644, add_slot_show, add_slot_store);

static struct kobj_attribute remove_slot_attr =
	__ATTR(REMOVE_SLOT_ATTR_NAME, 0644, remove_slot_show, remove_slot_store);

static struct attribute *default_attrs[] = {
	&add_slot_attr.attr,
	&remove_slot_attr.attr,
	NULL,
};

static struct attribute_group dlpar_attr_group = {
	.attrs = default_attrs,
};

static struct kobject *dlpar_kobj;

int dlpar_sysfs_init(void)
{
	int error;

	dlpar_kobj = kobject_create_and_add(DLPAR_KOBJ_NAME,
					    &pci_slots_kset->kobj);
	if (!dlpar_kobj)
		return -EINVAL;

	error = sysfs_create_group(dlpar_kobj, &dlpar_attr_group);
	if (error)
		kobject_put(dlpar_kobj);
	return error;
}

void dlpar_sysfs_exit(void)
{
	sysfs_remove_group(dlpar_kobj, &dlpar_attr_group);
	kobject_put(dlpar_kobj);
}


装载sysfs系统:dentry(有inode,有一个d_fsdata指针指向sysfs_dirent)和sysfs_dirent的关系, super_block,get_inode 。

注册sysfs子系统, 属性集关联到kobject, kobj_type, kset.

注册bus总线过程, kobject_create_and_add,  kset_create_and_add.

添加device在sysfs中的读写属性kobj_attribute, attribute_group, store, show, 。

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值