本篇文章不是以文件系统的角度来详细描述sysfs,而是从内核对象如何通过sysfs表示整个设备驱动模型为切入点,进一步理解Linux内核对象。
在上文《内核对象与对象集》中,将kobject添加到sysfs中,kobject_add –> kobject_add_varg –> kobject_add_internal,调用create_dir创建sysfs目录和属性文件。
static int create_dir(struct kobject *kobj)
{
const struct kobj_ns_type_operations *ops;
int error;
//调用sysfs接口创建kobject对应的目录
error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj));
if (error)
return error;
error = populate_dir(kobj); //在kobject对应的目录中生成默认属性文件
if (error) {
sysfs_remove_dir(kobj);
return error;
}
/*
* @kobj->sd may be deleted by an ancestor going away. Hold an
* extra reference so that it stays until @kobj is gone.
*/
sysfs_get(kobj->sd);
/*
* If @kobj has ns_ops, its children need to be filtered based on
* their namespace tags. Enable namespace support on @kobj->sd.
*/
ops = kobj_child_ns_ops(kobj);
if (ops) {
BUG_ON(ops->type <= KOBJ_NS_TYPE_NONE);
BUG_ON(ops->type >= KOBJ_NS_TYPES);
BUG_ON(!kobj_ns_type_registered(ops->type));
sysfs_enable_ns(kobj->sd);
}
return 0;
}
/*
* populate_dir - populate directory with attributes.
* @kobj: object we're working on.
*
* Most subsystems have a set of default attributes that are associated
* with an object that registers with them. This is a helper called during
* object registration that loops through the default attributes of the
* subsystem and creates attributes files for them in sysfs.
*/
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;
}
create_dir通过调用sysfs_create_dir_ns创建sysfs中的目录,调用sysfs_create_file创建属性文件。
kern_node代表sysfs中每个节点。
/*
* kernfs_node - the building block of kernfs hierarchy. Each and every
* kernfs node is represented by single kernfs_node. Most fields are
* private to kernfs and shouldn't be accessed directly by kernfs users.
*
* As long as s_count reference is held, the kernfs_node itself is
* accessible. Dereferencing elem or any other outer entity requires
* active reference.
*/
struct kernfs_node {
atomic_t count; //引用计数
atomic_t active; //活动的引用计数
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
/*
* Use kernfs_get_parent() and kernfs_name/path() instead of
* accessing the following two fields directly. If the node is
* never moved to a different parent, it is safe to access the
* parent directly.
*/
struct kernfs_node *parent; //指向父节点
const char *name; //节点名称,在sysfs显示的名字
struct rb_node rb; //接入sysfs红黑树的链接项
const void *ns; /* namespace tag */
unsigned int hash; /* ns + name hash 红黑树key */
union {
struct kernfs_elem_dir dir; //该kern_node类型为目录
struct kernfs_elem_symlink symlink; //该kern_node类型为链接
struct kernfs_elem_attr attr; //该kern_node类型为属性文件
};
void *priv;
unsigned short flags; //标记位,目录、链接、属性文件或是否已被删除
umode_t mode; //访问权限,在sysfs中该kern_node的权限
unsigned int ino; //唯一编号
struct kernfs_iattrs *iattr; //用于设置非默认的inode属性,如果没有则置为NULL
};
/**
* sysfs_create_dir_ns - create a directory for an object with a namespace tag
* @kobj: object we're creating directory for
* @ns: the namespace tag to use
*/
int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
{
struct kernfs_node *parent, *kn;
BUG_ON(!kobj);
if (kobj->parent)
parent = kobj->parent->sd; //如果kobject设置parent,则使用之
else
parent = sysfs_root_kn; //否则parent就设置为sysfs根目录
if (!parent)
return -ENOENT;
//创建目录
kn = kernfs_create_dir_ns(parent, kobject_name(kobj),
S_IRWXU | S_IRUGO | S_IXUGO, kobj, ns);
if (IS_ERR(kn)) {
if (PTR_ERR(kn) == -EEXIST)
sysfs_warn_dup(parent, kobject_name(kobj));
return PTR_ERR(kn);
}
kobj->sd = kn;
return 0;
}
/**
* kernfs_create_dir_ns - create a directory
* @parent: parent in which to create a new directory
* @name: name of the new directory
* @mode: mode of the new directory
* @priv: opaque data associated with the new directory
* @ns: optional namespace tag of the directory
*
* Returns the created node on success, ERR_PTR() value on failure.
*/
struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
const char *name, umode_t mode,
void *priv, const void *ns)
{
struct kernfs_node *kn;
int rc;
/* allocate 分配空间并初始化, KERNFS_DIR指定创建目录 */
kn = kernfs_new_node(parent, name, mode | S_IFDIR, KERNFS_DIR);
if (!kn)
return ERR_PTR(-ENOMEM);
kn->dir.root = parent->dir.root; //指向根目录kern_node
kn->ns = ns; //指定命名空间
kn->priv = priv;
/* link in */
rc = kernfs_add_one(kn); //将kern_node加入父目录的红黑树中
if (!rc)
return kn;
kernfs_put(kn);
return ERR_PTR(rc);
}
kernfs_create_dir_ns函数中的两个主要函数kernfs_new_node和kernfs_add_one,在创建文件和创建符号链接同样使用,仅是参数不同。
为kern_node结构分配空间,并初始化
struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
const char *name, umode_t mode,
unsigned flags)
{
struct kernfs_node *kn;
//分配kern_node空间,并初始化
kn = __kernfs_new_node(kernfs_root(parent), name, mode, flags);
if (kn) {
kernfs_get(parent);
kn->parent = parent;
}
return kn;
}
static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
const char *name, umode_t mode,
unsigned flags)
{
struct kernfs_node *kn;
int ret;
name = kstrdup_const(name, GFP_KERNEL); //复制常量字符串
if (!name)
return NULL;
kn = kmem_cache_zalloc(kernfs_node_cache, GFP_KERNEL); //在缓存空间分配kernfs_node
if (!kn)
goto err_out1;
/*
* If the ino of the sysfs entry created for a kmem cache gets
* allocated from an ida layer, which is accounted to the memcg that
* owns the cache, the memcg will get pinned forever. So do not account
* ino ida allocations.
*/
ret = ida_simple_get(&root->ino_ida, 1, 0, //获取唯一标号,用于唯一标示kern_node
GFP_KERNEL | __GFP_NOACCOUNT);
if (ret < 0)
goto err_out2;
kn->ino = ret;
atomic_set(&kn->count, 1); //更新引用计数
atomic_set(&kn->active, KN_DEACTIVATED_BIAS);
RB_CLEAR_NODE(&kn->rb);
//设置kern_node相关域
kn->name = name;
kn->mode = mode;
kn->flags = flags;
return kn;
err_out2:
kmem_cache_free(kernfs_node_cache, kn);
err_out1:
kfree_const(name);
return NULL;
}
将kern_node添加到parent的红黑树中:
/**
* kernfs_add_one - add kernfs_node to parent without warning
* @kn: kernfs_node to be added
*
* The caller must already have initialized @kn->parent. This
* function increments nlink of the parent's inode if @kn is a
* directory and link into the children list of the parent.
*
* RETURNS:
* 0 on success, -EEXIST if entry with the given name already
* exists.
*/
int kernfs_add_one(struct kernfs_node *kn)
{
struct kernfs_node *parent = kn->parent;
struct kernfs_iattrs *ps_iattr;
bool has_ns;
int ret;
mutex_lock(&kernfs_mutex);
ret = -EINVAL;
has_ns = kernfs_ns_enabled(parent);
if (WARN(has_ns != (bool)kn->ns, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n",
has_ns ? "required" : "invalid", parent->name, kn->name))
goto out_unlock;
if (kernfs_type(parent) != KERNFS_DIR) //检查parent是否为目录
goto out_unlock;
ret = -ENOENT;
if (parent->flags & KERNFS_EMPTY_DIR) //检查parent是否为空目录
goto out_unlock;
//检查parent是否是active状态
if ((parent->flags & KERNFS_ACTIVATED) && !kernfs_active(parent))
goto out_unlock;
kn->hash = kernfs_name_hash(kn->name, kn->ns); //作为红黑树比较的key
ret = kernfs_link_sibling(kn); //kern_node链入parent节点红黑树中
if (ret)
goto out_unlock;
/* Update timestamps on the parent */
ps_iattr = parent->iattr;
if (ps_iattr) {
struct iattr *ps_iattrs = &ps_iattr->ia_iattr;
ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;
}
mutex_unlock(&kernfs_mutex);
/*
* Activate the new node unless CREATE_DEACTIVATED is requested.
* If not activated here, the kernfs user is responsible for
* activating the node with kernfs_activate(). A node which hasn't
* been activated is not visible to userland and its removal won't
* trigger deactivation.
*/
if (!(kernfs_root(kn)->flags & KERNFS_ROOT_CREATE_DEACTIVATED))
kernfs_activate(kn);
return 0;
out_unlock:
mutex_unlock(&kernfs_mutex);
return ret;
}
sysfs红黑树中的key:
/**
* kernfs_name_hash
* @name: Null terminated string to hash
* @ns: Namespace tag to hash
*
* Returns 31 bit hash of ns + name (so it fits in an off_t )
*/
static unsigned int kernfs_name_hash(const char *name, const void *ns)
{
unsigned long hash = init_name_hash();
unsigned int len = strlen(name);
while (len--)
hash = partial_name_hash(*name++, hash);
hash = (end_name_hash(hash) ^ hash_ptr((void *)ns, 31));
hash &= 0x7fffffffU;
/* Reserve hash numbers 0, 1 and INT_MAX for magic directory entries */
if (hash < 2)
hash += 2;
if (hash >= INT_MAX)
hash = INT_MAX - 1;
return hash;
}
static int kernfs_name_compare(unsigned int hash, const char *name,
const void *ns, const struct kernfs_node *kn)
{
if (hash < kn->hash)
return -1;
if (hash > kn->hash)
return 1;
if (ns < kn->ns)
return -1;
if (ns > kn->ns)
return 1;
return strcmp(name, kn->name);
}
kern_node链入parent节点红黑树中:
/**
* kernfs_link_sibling - link kernfs_node into sibling rbtree
* @kn: kernfs_node of interest
*
* Link @kn into its sibling rbtree which starts from
* @kn->parent->dir.children.
*
* Locking:
* mutex_lock(kernfs_mutex)
*
* RETURNS:
* 0 on susccess -EEXIST on failure.
*/
static int kernfs_link_sibling(struct kernfs_node *kn)
{
struct rb_node **node = &kn->parent->dir.children.rb_node; //parent目录的红黑树
struct rb_node *parent = NULL;
while (*node) { //在parent的目录中,寻找合适的位置将kn插入parent的红黑树中
struct kernfs_node *pos;
int result;
pos = rb_to_kn(*node);
parent = *node;
result = kernfs_sd_compare(kn, pos); //优先顺序: hash > ns > name
if (result < 0)
node = &pos->rb.rb_left;
else if (result > 0)
node = &pos->rb.rb_right;
else
return -EEXIST;
}
/* add new node and rebalance the tree */
rb_link_node(&kn->rb, parent, node);
rb_insert_color(&kn->rb, &kn->parent->dir.children);
/* successfully added, account subdir number */
if (kernfs_type(kn) == KERNFS_DIR)
kn->parent->dir.subdirs++;
return 0;
}
static inline int __must_check sysfs_create_file(struct kobject *kobj,
const struct attribute *attr)
{
return sysfs_create_file_ns(kobj, attr, NULL);
}
/**
* sysfs_create_file_ns - create an attribute file for an object with custom ns
* @kobj: object we're creating for
* @attr: attribute descriptor
* @ns: namespace the new file should belong to
*/
int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr,
const void *ns)
{
BUG_ON(!kobj || !kobj->sd || !attr);
return sysfs_add_file_mode_ns(kobj->sd, attr, false, attr->mode, ns);
}
EXPORT_SYMBOL_GPL(sysfs_create_file_ns);
int sysfs_add_file_mode_ns(struct kernfs_node *parent,
const struct attribute *attr, bool is_bin,
umode_t mode, const void *ns)
{
struct lock_class_key *key = NULL;
const struct kernfs_ops *ops;
struct kernfs_node *kn;
loff_t size;
if (!is_bin) {
struct kobject *kobj = parent->priv;
const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops;
/* every kobject with an attribute needs a ktype assigned */
if (WARN(!sysfs_ops, KERN_ERR
"missing sysfs attribute operations for kobject: %s\n",
kobject_name(kobj)))
return -EINVAL;
//确定读写的操作函数
if (sysfs_ops->show && sysfs_ops->store) {
if (mode & SYSFS_PREALLOC)
ops = &sysfs_prealloc_kfops_rw;
else
ops = &sysfs_file_kfops_rw;
} else if (sysfs_ops->show) {
if (mode & SYSFS_PREALLOC)
ops = &sysfs_prealloc_kfops_ro;
else
ops = &sysfs_file_kfops_ro;
} else if (sysfs_ops->store) {
if (mode & SYSFS_PREALLOC)
ops = &sysfs_prealloc_kfops_wo;
else
ops = &sysfs_file_kfops_wo;
} else
ops = &sysfs_file_kfops_empty;
size = PAGE_SIZE;
} else {
struct bin_attribute *battr = (void *)attr;
if (battr->mmap)
ops = &sysfs_bin_kfops_mmap;
else if (battr->read && battr->write)
ops = &sysfs_bin_kfops_rw;
else if (battr->read)
ops = &sysfs_bin_kfops_ro;
else if (battr->write)
ops = &sysfs_bin_kfops_wo;
else
ops = &sysfs_file_kfops_empty;
size = battr->size;
}
#ifdef CONFIG_DEBUG_LOCK_ALLOC
if (!attr->ignore_lockdep)
key = attr->key ?: (struct lock_class_key *)&attr->skey;
#endif
kn = __kernfs_create_file(parent, attr->name, mode & 0777, size, ops,
(void *)attr, ns, key); //创建属性文件
if (IS_ERR(kn)) {
if (PTR_ERR(kn) == -EEXIST)
sysfs_warn_dup(parent, attr->name);
return PTR_ERR(kn);
}
return 0;
}
通过上面的代码跟踪,创建属性文件由__kernfs_create_file实现,最终仍然是调用kernfs_new_node和kernfs_add_one。
/**
* __kernfs_create_file - kernfs internal function to create a file
* @parent: directory to create the file in
* @name: name of the file
* @mode: mode of the file
* @size: size of the file
* @ops: kernfs operations for the file
* @priv: private data for the file
* @ns: optional namespace tag of the file
* @key: lockdep key for the file's active_ref, %NULL to disable lockdep
*
* Returns the created node on success, ERR_PTR() value on error.
*/
struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
const char *name,
umode_t mode, loff_t size,
const struct kernfs_ops *ops,
void *priv, const void *ns,
struct lock_class_key *key)
{
struct kernfs_node *kn;
unsigned flags;
int rc;
flags = KERNFS_FILE; //创建的kern_node类型为file
//分配空间并初始化
kn = kernfs_new_node(parent, name, (mode & S_IALLUGO) | S_IFREG, flags);
if (!kn)
return ERR_PTR(-ENOMEM);
kn->attr.ops = ops;
kn->attr.size = size;
kn->ns = ns;
kn->priv = priv;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
if (key) {
lockdep_init_map(&kn->dep_map, "s_active", key, 0);
kn->flags |= KERNFS_LOCKDEP;
}
#endif
/*
* kn->attr.ops is accesible only while holding active ref. We
* need to know whether some ops are implemented outside active
* ref. Cache their existence in flags.
*/
if (ops->seq_show)
kn->flags |= KERNFS_HAS_SEQ_SHOW;
if (ops->mmap)
kn->flags |= KERNFS_HAS_MMAP;
rc = kernfs_add_one(kn); //将kern_node添加到parent的红黑树中
if (rc) {
kernfs_put(kn);
return ERR_PTR(rc);
}
return kn;
}
在sysfs_add_file_mode_ns函数中根据flags的不同,注册不同的读写回调函数,下面以sysfs_prealloc_kfops_rw函数为例,其他结构类似,不赘述。
//常规文件--sysfs_prealloc_kfops_rw
static const struct kernfs_ops sysfs_prealloc_kfops_rw = {
.read = sysfs_kf_read,
.write = sysfs_kf_write,
.prealloc = true,
};
/* kernfs read callback for regular sysfs files with pre-alloc */
static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf,
size_t count, loff_t pos)
{
const struct sysfs_ops *ops = sysfs_file_ops(of->kn); //获取kobject中的sysfs_ops操作表
struct kobject *kobj = of->kn->parent->priv;
size_t len;
/*
* If buf != of->prealloc_buf, we don't know how
* large it is, so cannot safely pass it to ->show
*/
if (pos || WARN_ON_ONCE(buf != of->prealloc_buf))
return 0;
len = ops->show(kobj, of->kn->priv, buf); //kobject中sd域的sysfs_ops操作表中的show
return min(count, len);
}
/* kernfs write callback for regular sysfs files */
static ssize_t sysfs_kf_write(struct kernfs_open_file *of, char *buf,
size_t count, loff_t pos)
{ //获取kobject中的sysfs_ops操作表
const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
struct kobject *kobj = of->kn->parent->priv;
if (!count)
return 0;
return ops->store(kobj, of->kn->priv, buf, count); //kobject中sd域的sysfs_ops操作表中的store
}
关于属性文件的读写操作,最终都回调到kobject中的sd域的sysfs_ops操作表,这个操作表示在kobject_init函数中设置。回顾kobject_create函数:
struct kobject *kobject_create(void)
{
struct kobject *kobj;
kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); //分配空间
if (!kobj)
return NULL;
kobject_init(kobj, &dynamic_kobj_ktype); //初始化, kobj_type类型为dynamic_kobj_ktype
return kobj;
}
//注册如下结构
static struct kobj_type dynamic_kobj_ktype = {
.release = dynamic_kobj_release,
.sysfs_ops = &kobj_sysfs_ops,
};
const struct sysfs_ops kobj_sysfs_ops = {
.show = kobj_attr_show,
.store = kobj_attr_store,
};
EXPORT_SYMBOL_GPL(kobj_sysfs_ops);
kobject的sysfs的show和store方法为:kobj_attr_show和kobj_attr_store
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) //如果业务子系统设置了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) //如果业务子系统设置了store函数,则调用
ret = kattr->store(kobj, kattr, buf, count);
return ret;
}
真正的对属性文件进行读写的回调由业务子系统实现。
/**
* sysfs_create_link - create symlink between two objects.
* @kobj: object whose directory we're creating the link in.
* @target: object we're pointing to.
* @name: name of the symlink.
*/
int sysfs_create_link(struct kobject *kobj, struct kobject *target,
const char *name)
{
return sysfs_do_create_link(kobj, target, name, 1);
}
EXPORT_SYMBOL_GPL(sysfs_create_link);
static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target,
const char *name, int warn)
{
struct kernfs_node *parent = NULL;
if (!kobj)
parent = sysfs_root_kn;
else
parent = kobj->sd;
if (!parent)
return -EFAULT;
return sysfs_do_create_link_sd(parent, target, name, warn);
}
static int sysfs_do_create_link_sd(struct kernfs_node *parent,
struct kobject *target_kobj,
const char *name, int warn)
{
struct kernfs_node *kn, *target = NULL;
BUG_ON(!name || !parent);
/*
* We don't own @target_kobj and it may be removed at any time.
* Synchronize using sysfs_symlink_target_lock. See
* sysfs_remove_dir() for details.
*/
spin_lock(&sysfs_symlink_target_lock);
if (target_kobj->sd) {
target = target_kobj->sd;
kernfs_get(target);
}
spin_unlock(&sysfs_symlink_target_lock);
if (!target)
return -ENOENT;
kn = kernfs_create_link(parent, name, target); //创建sysfs符号链接
kernfs_put(target);
if (!IS_ERR(kn))
return 0;
if (warn && PTR_ERR(kn) == -EEXIST)
sysfs_warn_dup(parent, name);
return PTR_ERR(kn);
}
由上面的代码追踪,创建符号链接由kernfs_create_link函数上。
/**
* kernfs_create_link - create a symlink
* @parent: directory to create the symlink in
* @name: name of the symlink
* @target: target node for the symlink to point to
*
* Returns the created node on success, ERR_PTR() value on error.
*/
struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
const char *name,
struct kernfs_node *target)
{
struct kernfs_node *kn;
int error;
//指定创建符号链接
kn = kernfs_new_node(parent, name, S_IFLNK|S_IRWXUGO, KERNFS_LINK);
if (!kn)
return ERR_PTR(-ENOMEM);
if (kernfs_ns_enabled(parent))
kn->ns = target->ns;
kn->symlink.target_kn = target;
kernfs_get(target); /* ref owned by symlink */
error = kernfs_add_one(kn); //将kern_node添加到parent的红黑树中
if (!error)
return kn;
kernfs_put(kn);
return ERR_PTR(error);
}
与创建目录和文件类似,最终仍然是调用kernfs_new_node和kernfs_add_one实现。
目标:在sysfs中创建一个目录/sys/kernel/storage/,在该目录下,还创建了一个文件value。value可以写入整型数据,随后可以读出。
* 定义内核对象
struct storage_obj {
struct kobject kobj;
int val; //用于保存写入的数据
};
struct storage_attribute {
struct attribute *attr;
ssize_t (*show)(struct kobject *, struct attribute *, char *);
ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
}
//定义并初始化storage_attribute
struct storage_attribute *sattr = &struct storage_attribute {
.attr = {.name = "value", .mode = 0666},
.show = storage_show,
.store = storage_store,
};
ssize_t storage_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
struct storage *stor = container_of(kobj, struct storage_obj, kobj);
stor->val = atoi(buf);
}
ssize_t storage_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t s) {
struct storage *stor = container_of(kobj, struct storage_obj, kobj);
memcpy(buf, s, itoa(stor->val));
}
void storage_release(struct kobject *kobj)
{
......
}
struct storage_ktype {
struct kobj_type *ktype;
}
int storage_create_file(struct storage_obj *sobj, const struct storage_attribute *attr)
{
int error = 0;
if (sobj) {
error = sysfs_create_file(&sobj->kobj, &attr->attr);
}
return error;
}
void storage_remove_file(struct storage_obj *sobj, const struct storage_attribute *attr)
{
if (sobj) {
sysfs_remove_file(&sobj->kobj, &attr->attr);
}
}
struct storage_obj * create_storage_obj()
{
struct storage_obj *sobj = (struct storage_obj *)malloc(struct storage_obj);
struct storage_ktype *stype = (struct storage_ktype *)malloc(struct storage_ktype);
sobj->parent = kernel_kobj;
kobject_init_and_add(&sobj->kobj, &stype->ktype);
return sobj
}
void destroy_storage_obj(struct kobject *kobj) {
struct storage_obj *sobj = container_of(kobj, struct storage_obj, kobj);
kobject_del(kboj);
free(sobj);
free(stype);
}