kobject:最小的device module unit,内嵌在kernel的device资源结构中,例如 character device(cdev), block device(blkdev)。这些资料结构中都会内嵌一个kobject。
struct kobject {
const char *name;
/*该Kobject的名称,同时也是sysfs中的目录名称。由于Kobject添加到Kernel时,需要根据名字注册到sysfs中,之后就不能再直接修改该字段。如果需要修改Kobject的名字,需要调用kobject_rename接口,该接口会主动处理sysfs的相关事宜。*/
struct list_head entry;
//用于将Kobject加入到Kset中的list_head
struct kobject *parent;
//指向parent kobject,以此形成层次结构(在sysfs就表现为目录结构)
struct kset *kset;
//该kobject属于的Kset。可以为NULL。如果存在,且没有指定parent,则会把Kset作为parent(Kset是一个特殊的Kobject),kset较kobj_type优先级高
struct kobj_type *ktype;
//该Kobject属于的kobj_type。每个Kobject必须有一个ktype,或者Kernel会提示错误
struct sysfs_dirent *sd;//该Kobject在sysfs中的表示
struct kref kref;
//"struct kref”类型(在include/linux/kref.h中定义)的变量,为一个可用于原子操作的引用计数
unsigned int state_initialized:1;
//指示该Kobject是否已经初始化,以在Kobject的Init,Put,Add等操作时进行异常校验
unsigned int state_in_sysfs:1;
//指示该Kobject是否已在sysfs中呈现,以便在自动注销时从sysfs中移除
unsigned int state_add_uevent_sent:1;
//记录是否已经向用户空间发送ADD uevent,如果有,且没有发送remove uevent,则在自动注销时,补发REMOVE uevent,以便让用户空间正确处理
unsigned int state_remove_uevent_sent:1;//
unsigned int uevent_suppress:1;
//如果该字段为1,则表示忽略所有上报的uevent事件
};
ktype:kobject的集合,偏向kobject的操作集合,负责管理kobjects的operation。kobject会利用它辨识自己属于哪一个类型,然后在/sys下建立正确的目录位置。
struct kobj_type {
void (*release)(struct kobject *kobj);
//通过该回调函数,可以将包含该种类型kobject的数据结构的内存空间释放掉
struct sysfs_ops *sysfs_ops;
//该种类型的Kobject的sysfs文件系统接口
struct attribute **default_attrs;
//该种类型的Kobject的atrribute列表(所谓attribute,就是sysfs文件系统中的文件)。将会在Kobject添加到内核时,一并注册到sysfs中。
};
kset:kobject的集合,建立上层(sub-system)和下层(kobject)的关联性,kobject会利用它辨识自己属于哪一个类型,然后再/sys下建立正确的目录位置。kset的优先级比较高,kobject会利用自己的kset找到自己所属的kset,并把*ktype制定成该kset下的ktype.
struct kset {
struct list_head list;//用于保存该kset下所有的kobject的链表
spinlock_t list_lock;
struct kobject kobj;
//该kset自己的kobject(kset是一个特殊的kobject,也会在sysfs中以目录的形式体现)
struct kset_uevent_ops *uevent_ops;
//该kset的uevent操作函数集。当任何Kobject需要上报uevent时,都要调用它所从属的kset的uevent_ops,添加环境变量,或者过滤event(kset可以决定哪些event可以上报)。因此,如果一个kobject不属于任何kset时,是不允许发送uevent的
};
subsystem:管理kset的集合
attribute:建立了kobject并成功之后,该kobj对应的目录需要使用attribute产生资料数据,其机构如下:
struct attribute {
char *name;
struct module *owner; // THIS_MODULE
mode_t mode; //permission, "S_IRUGO" or "S_IWUSR" or "0660"
};
kobject_event.c分析
/*
* kernel userspace event delivery
*
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
* Copyright (C) 2004 Novell, Inc. All rights reserved.
* Copyright (C) 2004 IBM, Inc. All rights reserved.
*
* Licensed under the GNU GPL v2.
*
* Authors:
* Robert Love <rml@novell.com>
* Kay Sievers <kay.sievers@vrfy.org>
* Arjan van de Ven <arjanv@redhat.com>
* Greg Kroah-Hartman <greg@kroah.com>
*/
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/socket.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <net/sock.h>
u64 uevent_seqnum;
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
static DEFINE_SPINLOCK(sequence_lock);
#if defined(CONFIG_NET)
static struct sock *uevent_sock;
#endif
/* the strings here must match the enum in include/linux/kobject.h */
static const char *kobject_actions[] = {
[KOBJ_ADD] = "add",
[KOBJ_REMOVE] = "remove",
[KOBJ_CHANGE] = "change",
[KOBJ_MOVE] = "move",
[KOBJ_ONLINE] = "online",
[KOBJ_OFFLINE] = "offline",
};
/**
* kobject_action_type - translate action string to numeric type
*
* @buf: buffer containing the action string, newline is ignored
* @len: length of buffer
* @type: pointer to the location to store the action type
*
* Returns 0 if the action string was recognized.
*/
int kobject_action_type(const char *buf, size_t count,
enum kobject_action *type)
{
enum kobject_action action;
int ret = -EINVAL;
if (count && (buf[count-1] == '\n' || buf[count-1] == '\0'))
count--;
if (!count)
goto out;
for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) {
if (strncmp(kobject_actions[action], buf, count) != 0)
continue;
if (kobject_actions[action][count] != '\0')
continue;
*type = action;
ret = 0;
break;
}
out:
return ret;
}
/**
* kobject_uevent_env - send an uevent with environmental data
*
* @action: action that is happening
* @kobj: struct kobject that the action is happening to
* @envp_ext: pointer to environmental data
*
* Returns 0 if kobject_uevent() is completed with success or the
* corresponding error when it fails.
*/
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
char *envp_ext[])//发送环境数据
{
/*
* 查找kobj本身或者其parent是否从属于某个kset,如果不是,则报错返回
* 查看kobj->uevent_suppress是否设置,如果设置,则忽略所有的uevent上报并返回
* 如果所属的kset有uevent_ops->filter函数,则调用该函数,过滤此次上报
* 判断所属的kset是否有合法的名称(称作subsystem,和前期的内核版本有区别),否则不允许上报uevent
* 分配一个用于此次上报的、存储环境变量的buffer(结果保存在env指针中),并获得该Kobject在sysfs中路径信息(用户空间软件需要依据该路径信息在sysfs中访问它)
* 调用add_uevent_var接口,将Action、路径信息、subsystem等信息,添加到env指针中
* 如果传入的envp不空,则解析传入的环境变量中,同样调用add_uevent_var接口,添加到env指针中
* 如果所属的kset存在uevent_ops->uevent接口,调用该接口,添加kset统一的环境变量到env指针
* 根据ACTION的类型,设置kobj->state_add_uevent_sent和kobj->state_remove_uevent_sent变量,以记录正确的状态
* 调用add_uevent_var接口,添加格式为"SEQNUM=%llu”的序列号
* 如果定义了"CONFIG_NET”,则使用netlink发送该uevent
* 以uevent_helper、subsystem以及添加了标准环境变量(HOME=/,PATH=/sbin:/bin:/usr/sbin:/usr/bin)的env指针为参数,调用kmod模块提供的call_usermodehelper函数,上报uevent。?其中uevent_helper的内容是由内核配置项CONFIG_UEVENT_HELPER_PATH(位于./drivers/base/Kconfig)决定的(可参考lib/kobject_uevent.c, line 32),该配置项指定了一个用户空间程序(或者脚本),用于解析上报的uevent,例如"/sbin/hotplug”。?call_usermodehelper的作用,就是fork一个进程,以uevent为参数,执行uevent_helper。
*/
struct kobj_uevent_env *env;
const char *action_string = kobject_actions[action];
const char *devpath = NULL;
const char *subsystem;
struct kobject *top_kobj;
struct kset *kset;
struct kset_uevent_ops *uevent_ops;
u64 seq;
int i = 0;
int retval = 0;
pr_debug("kobject: '%s' (%p): %s\n",
kobject_name(kobj), kobj, __func__);
/* search the kset we belong to */
top_kobj = kobj;
while (!top_kobj->kset && top_kobj->parent)//若kset不存在,则查找其父节点的kset是否存在,不沉溺在则继续查找父节点
top_kobj = top_kobj->parent;
if (!top_kobj->kset) {
pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
"without kset!\n", kobject_name(kobj), kobj,
__func__); //最终没找到就报错
return -EINVAL;
}
kset = top_kobj->kset;//找到之于相关的kset
uevent_ops = kset->uevent_ops;
/* skip the event, if uevent_suppress is set*/
if (kobj->uevent_suppress) {
pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
"caused the event to drop!\n",
kobject_name(kobj), kobj, __func__);
return 0;
}
/* skip the event, if the filter returns zero. */
if (uevent_ops && uevent_ops->filter)
if (!uevent_ops->filter(kset, kobj)) {
pr_debug("kobject: '%s' (%p): %s: filter function "
"caused the event to drop!\n",
kobject_name(kobj), kobj, __func__);
return 0;
}
/* originating subsystem */
if (uevent_ops && uevent_ops->name)
subsystem = uevent_ops->name(kset, kobj);//name函数存在,则使用kset返回的kset的name
else
subsystem = kobject_name(&kset->kobj);//否则用kset里kobj的name做kset的name
if (!subsystem) {
pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
"event to drop!\n", kobject_name(kobj), kobj,
__func__);
return 0;
}
/* environment buffer */
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);//分配一个环境变量结构体
if (!env)
return -ENOMEM;
/* complete object path */
devpath = kobject_get_path(kobj, GFP_KERNEL);//根据kobj获取sysfs中的路径
if (!devpath) {
retval = -ENOENT;
goto exit;
}
/* default keys */ //添加当前要上报的行为,path,name到env的buffer中
retval = add_uevent_var(env, "ACTION=%s", action_string);//上报的操作行为
if (retval)
goto exit;
retval = add_uevent_var(env, "DEVPATH=%s", devpath);//路径
if (retval)
goto exit;
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);//name
if (retval)
goto exit;
/* keys passed in from the caller */
if (envp_ext) { //添加外部环境变量到buffer中
for (i = 0; envp_ext[i]; i++) {
retval = add_uevent_var(env, "%s", envp_ext[i]);
if (retval)
goto exit;
}
}
/* let the kset specific function add its stuff */
if (uevent_ops && uevent_ops->uevent) {
retval = uevent_ops->uevent(kset, kobj, env);//调用uevent发送环境数据
if (retval) {
pr_debug("kobject: '%s' (%p): %s: uevent() returned "
"%d\n", kobject_name(kobj), kobj,
__func__, retval);
goto exit;
}
}
/*
* Mark "add" and "remove" events in the object to ensure proper
* events to userspace during automatic cleanup. If the object did
* send an "add" event, "remove" will automatically generated by
* the core, if not already done by the caller.
*/
if (action == KOBJ_ADD)
kobj->state_add_uevent_sent = 1;
else if (action == KOBJ_REMOVE)
kobj->state_remove_uevent_sent = 1; //改变kobj中状态
/* we will send an event, so request a new sequence number */
spin_lock(&sequence_lock);
seq = ++uevent_seqnum;
spin_unlock(&sequence_lock);
retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
if (retval)
goto exit;
#if defined(CONFIG_NET)
/* send netlink message */
if (uevent_sock) {
struct sk_buff *skb;
size_t len;
/* allocate message with the maximum possible size */
len = strlen(action_string) + strlen(devpath) + 2;
skb = alloc_skb(len + env->buflen, GFP_KERNEL);
if (skb) {
char *scratch;
/* add header */
scratch = skb_put(skb, len);//将长度添加到buffer中即skb
sprintf(scratch, "%s@%s", action_string, devpath);
/* copy keys to our continuous event payload buffer */
for (i = 0; i < env->envp_idx; i++) {
len = strlen(env->envp[i]) + 1;
scratch = skb_put(skb, len);
strcpy(scratch, env->envp[i]);
}
NETLINK_CB(skb).dst_group = 1;
retval = netlink_broadcast(uevent_sock, skb, 0, 1,
GFP_KERNEL); //事件上报给用户空间
/* ENOBUFS should be handled in userspace */
if (retval == -ENOBUFS)
retval = 0;
} else
retval = -ENOMEM;
}
#endif
/* call uevent_helper, usually only enabled during early boot */
if (uevent_helper[0]) {
char *argv [3];
argv [0] = uevent_helper;
argv [1] = (char *)subsystem;
argv [2] = NULL;
retval = add_uevent_var(env, "HOME=/");
if (retval)
goto exit;
retval = add_uevent_var(env,
"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
if (retval)
goto exit;
retval = call_usermodehelper(argv[0], argv,
env->envp, UMH_WAIT_EXEC);
}
exit:
kfree(devpath);
kfree(env);
return retval;
}
EXPORT_SYMBOL_GPL(kobject_uevent_env);
/**
* kobject_uevent - notify userspace by ending an uevent
*
* @action: action that is happening
* @kobj: struct kobject that the action is happening to
*
* Returns 0 if kobject_uevent() is completed with success or the
* corresponding error when it fails.
*/
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
return kobject_uevent_env(kobj, action, NULL);
}
EXPORT_SYMBOL_GPL(kobject_uevent);
/**
* add_uevent_var - add key value string to the environment buffer
* @env: environment buffer structure
* @format: printf format for the key=value pair
*
* Returns 0 if environment variable was added successfully or -ENOMEM
* if no space was available.
*/
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...) //以格式化字符的形式,将环境变量copy到env指针中
{
va_list args;
int len;
if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
WARN(1, KERN_ERR "add_uevent_var: too many keys\n");
return -ENOMEM;
}
va_start(args, format);
len = vsnprintf(&env->buf[env->buflen],
sizeof(env->buf) - env->buflen,
format, args);
va_end(args);
if (len >= (sizeof(env->buf) - env->buflen)) {
WARN(1, KERN_ERR "add_uevent_var: buffer size too small\n");
return -ENOMEM;
}
env->envp[env->envp_idx++] = &env->buf[env->buflen];
env->buflen += len + 1;
return 0;
}
EXPORT_SYMBOL_GPL(add_uevent_var);
#if defined(CONFIG_NET)
static int __init kobject_uevent_init(void)
{
uevent_sock = netlink_kernel_create(&init_net, NETLINK_KOBJECT_UEVENT,
1, NULL, NULL, THIS_MODULE);
if (!uevent_sock) {
printk(KERN_ERR
"kobject_uevent: unable to create netlink socket!\n");
return -ENODEV;
}
netlink_set_nonroot(NETLINK_KOBJECT_UEVENT, NL_NONROOT_RECV);
return 0;
}
postcore_initcall(kobject_uevent_init);
#endif
kobject.c分析
/*
* kobject.c - library routines for handling generic kernel objects
*
* Copyright (c) 2002-2003 Patrick Mochel <mochel@osdl.org>
* Copyright (c) 2006-2007 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (c) 2006-2007 Novell Inc.
*
* This file is released under the GPLv2.
*
*
* Please see the file Documentation/kobject.txt for critical information
* about using the kobject interface.
*/
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/slab.h>
/*
* 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;
}
static int create_dir(struct kobject *kobj)
{
int error = 0;
if (kobject_name(kobj)) { //创建的目录的名字即为该kobj的name
error = sysfs_create_dir(kobj);
if (!error) {
error = populate_dir(kobj);
if (error)
sysfs_remove_dir(kobj);
}
}
return error;
}
static int get_kobj_path_length(struct kobject *kobj)
{
int length = 1;
struct kobject *parent = kobj;
/* walk up the ancestors until we hit the one pointing to the
* root.
* Add 1 to strlen for leading '/' of each level.
*/
do {
if (kobject_name(parent) == NULL)
return 0;
length += strlen(kobject_name(parent)) + 1;
parent = parent->parent;
} while (parent);
return length;
}
static void fill_kobj_path(struct kobject *kobj, char *path, int length)
{
struct kobject *parent;
--length;
for (parent = kobj; parent; parent = parent->parent) {
int cur = strlen(kobject_name(parent));
/* back up enough to print this name with '/' */
length -= cur;
strncpy(path + length, kobject_name(parent), cur);
*(path + --length) = '/';
}
pr_debug("kobject: '%s' (%p): %s: path = '%s'\n", kobject_name(kobj),
kobj, __func__, path);
}
/**
* kobject_get_path - generate and return the path associated with a given kobj and kset pair.
*
* @kobj: kobject in question, with which to build the path
* @gfp_mask: the allocation type used to allocate the path
*
* The result must be freed by the caller with kfree().
*/
char *kobject_get_path(struct kobject *kobj, gfp_t gfp_mask)
{
char *path;
int len;
len = get_kobj_path_length(kobj);
if (len == 0)
return NULL;
path = kzalloc(len, gfp_mask);
if (!path)
return NULL;
fill_kobj_path(kobj, path, len);
return path;
}
EXPORT_SYMBOL_GPL(kobject_get_path);
/* add the kobject to its kset's list */
static void kobj_kset_join(struct kobject *kobj)
{
if (!kobj->kset)
return;
kset_get(kobj->kset);
spin_lock(&kobj->kset->list_lock);
list_add_tail(&kobj->entry, &kobj->kset->list);//将kobj的entry添加到kset链表中是,其中entry用于将kobject添加到kset中的list_head
spin_unlock(&kobj->kset->list_lock);
}
/* remove the kobject from its kset's list */
static void kobj_kset_leave(struct kobject *kobj)
{
if (!kobj->kset)
return;
spin_lock(&kobj->kset->list_lock);
list_del_init(&kobj->entry);//删除kobject在kset中的节点
spin_unlock(&kobj->kset->list_lock);
kset_put(kobj->kset);//引用计数减1
}
static void kobject_init_internal(struct kobject *kobj)
{
if (!kobj)
return;
kref_init(&kobj->kref);//初始化kref为1.kref为源自操作的引用计数
INIT_LIST_HEAD(&kobj->entry);//初始化双向链表
kobj->state_in_sysfs = 0;//代表该kobject未在sysfs中呈现
kobj->state_add_uevent_sent = 0;//还未向用户空间发送add uevent
kobj->state_remove_uevent_sent = 0;//没有发送remove uevent
kobj->state_initialized = 1;//标记被初始化过
}
static int kobject_add_internal(struct kobject *kobj)//将kobject添加到kernel
{
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);//如果有父节点,父节点引用计数加1
/* join kset if set, use it as parent if we do not already have one */
if (kobj->kset) {
if (!parent)//如果kobject的父节点不存在,使用kset中的kobject作为父节点,并计数
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj);//将kobject添加到kset链表中
kobj->parent = parent;//给kobject添加父节点
}
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);//调用sysfs文件系统接口创建一个名为kobject->name的文件夹,目录与parent有直接关系
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;
}
/**
* kobject_set_name_vargs - Set the name of an kobject
* @kobj: struct kobject to set the name of
* @fmt: format string used to build the name
* @vargs: vargs to format the string.
*/
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
va_list vargs)
{
const char *old_name = kobj->name;
char *s;
if (kobj->name && !fmt)
return 0;
kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
if (!kobj->name)
return -ENOMEM;
/* ewww... some of these buggers have '/' in the name ... */
while ((s = strchr(kobj->name, '/')))
s[0] = '!';
kfree(old_name);
return 0;
}
/**
* kobject_set_name - Set the name of a kobject
* @kobj: struct kobject to set the name of
* @fmt: format string used to build the name
*
* This sets the name of the kobject. If you have already added the
* kobject to the system, you must call kobject_rename() in order to
* change the name of the kobject.
*/
int kobject_set_name(struct kobject *kobj, const char *fmt, ...)
{
va_list vargs;
int retval;
va_start(vargs, fmt);
retval = kobject_set_name_vargs(kobj, fmt, vargs);
va_end(vargs);
return retval;
}
EXPORT_SYMBOL(kobject_set_name);
/**
* kobject_init - initialize a kobject structure
* @kobj: pointer to the kobject to initialize
* @ktype: pointer to the ktype for this kobject.
*
* This function will properly initialize a kobject such that it can then
* be passed to the kobject_add() call.
*
* After this function is called, the kobject MUST be cleaned up by a call
* to kobject_put(), not by a call to kfree directly to ensure that all of
* the memory is cleaned up properly.
*/
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
/*
* 确认kobj和ktype不为空
* 如果该指针已经初始化过(判断kobj->state_initialized),打印错误提示及堆栈信息(但不是致命错误,所以还可以继续)
* 初始化kobj内部的参数,包括引用计数、list、各种标志等
* 根据输入参数,将ktype指针赋予kobj->ktype
*/
char *err_str;
if (!kobj) {
err_str = "invalid kobject pointer!";
goto error;
}
if (!ktype) {
err_str = "must have a ktype to be initialized properly!\n";
goto error;
}
if (kobj->state_initialized) {
/* do not error out as sometimes we can recover */
printk(KERN_ERR "kobject (%p): tried to init an initialized "
"object, something is seriously wrong.\n", kobj);
dump_stack();
}
kobject_init_internal(kobj);//初始化kobject,并对相应的状态进行设置
kobj->ktype = ktype;//关联kobj和ktype
return;
error:
printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
dump_stack();
}
EXPORT_SYMBOL(kobject_init);
static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
const char *fmt, va_list vargs)
{
int retval;
retval = kobject_set_name_vargs(kobj, fmt, vargs);//给kobject添加名字
if (retval) {
printk(KERN_ERR "kobject: can not set name properly!\n");
return retval;
}
kobj->parent = parent;
return kobject_add_internal(kobj);
}
/**
* kobject_add - the main kobject add function
* @kobj: the kobject to add
* @parent: pointer to the parent of the kobject.
* @fmt: format to name the kobject with.
*
* The kobject name is set and added to the kobject hierarchy in this
* function.
*
* If @parent is set, then the parent of the @kobj will be set to it.
* If @parent is NULL, then the parent of the @kobj will be set to the
* kobject associted with the kset assigned to this kobject. If no kset
* is assigned to the kobject, then the kobject will be located in the
* root of the sysfs tree.
*
* If this function returns an error, kobject_put() must be called to
* properly clean up the memory associated with the object.
* Under no instance should the kobject that is passed to this function
* be directly freed with a call to kfree(), that can leak memory.
*
* Note, no "add" uevent will be created with this call, the caller should set
* up all of the necessary sysfs files for the object and then call
* kobject_uevent() with the UEVENT_ADD parameter to ensure that
* userspace is properly notified of this kobject's creation.
*/
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...)//将初始化完成的kobject添加到内核
{
va_list args;
int retval;
if (!kobj)
return -EINVAL;
if (!kobj->state_initialized) {
printk(KERN_ERR "kobject '%s' (%p): tried to add an "
"uninitialized object, something is seriously wrong.\n",
kobject_name(kobj), kobj);
dump_stack();
return -EINVAL;
}
va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args);
va_end(args);
return retval;
}
EXPORT_SYMBOL(kobject_add);
/**
* kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy
* @kobj: pointer to the kobject to initialize
* @ktype: pointer to the ktype for this kobject.
* @parent: pointer to the parent of this kobject.
* @fmt: the name of the kobject.
*
* This function combines the call to kobject_init() and
* kobject_add(). The same type of error handling after a call to
* kobject_add() and kobject lifetime rules are the same here.
*/
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;
}
EXPORT_SYMBOL_GPL(kobject_init_and_add);
/**
* kobject_rename - change the name of an object
* @kobj: object in question.
* @new_name: object's new name
*
* It is the responsibility of the caller to provide mutual
* exclusion between two different calls of kobject_rename
* on the same kobject and to ensure that new_name is valid and
* won't conflict with other kobjects.
*/
int kobject_rename(struct kobject *kobj, const char *new_name)
{
int error = 0;
const char *devpath = NULL;
const char *dup_name = NULL, *name;
char *devpath_string = NULL;
char *envp[2];
kobj = kobject_get(kobj);
if (!kobj)
return -EINVAL;
if (!kobj->parent)
return -EINVAL;
devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
error = -ENOMEM;
goto out;
}
devpath_string = kmalloc(strlen(devpath) + 15, GFP_KERNEL);
if (!devpath_string) {
error = -ENOMEM;
goto out;
}
sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);
envp[0] = devpath_string;
envp[1] = NULL;
name = dup_name = kstrdup(new_name, GFP_KERNEL);
if (!name) {
error = -ENOMEM;
goto out;
}
error = sysfs_rename_dir(kobj, new_name);
if (error)
goto out;
/* Install the new kobject name */
dup_name = kobj->name;
kobj->name = name;
/* This function is mostly/only used for network interface.
* Some hotplug package track interfaces by their name and
* therefore want to know when the name is changed by the user. */
kobject_uevent_env(kobj, KOBJ_MOVE, envp);
out:
kfree(dup_name);
kfree(devpath_string);
kfree(devpath);
kobject_put(kobj);
return error;
}
EXPORT_SYMBOL_GPL(kobject_rename);
/**
* kobject_move - move object to another parent
* @kobj: object in question.
* @new_parent: object's new parent (can be NULL)
*/
int kobject_move(struct kobject *kobj, struct kobject *new_parent)
{
int error;
struct kobject *old_parent;
const char *devpath = NULL;
char *devpath_string = NULL;
char *envp[2];
kobj = kobject_get(kobj);
if (!kobj)
return -EINVAL;
new_parent = kobject_get(new_parent);
if (!new_parent) {
if (kobj->kset)
new_parent = kobject_get(&kobj->kset->kobj);
}
/* old object path */
devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
error = -ENOMEM;
goto out;
}
devpath_string = kmalloc(strlen(devpath) + 15, GFP_KERNEL);
if (!devpath_string) {
error = -ENOMEM;
goto out;
}
sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);
envp[0] = devpath_string;
envp[1] = NULL;
error = sysfs_move_dir(kobj, new_parent);
if (error)
goto out;
old_parent = kobj->parent;
kobj->parent = new_parent;
new_parent = NULL;
kobject_put(old_parent);
kobject_uevent_env(kobj, KOBJ_MOVE, envp);
out:
kobject_put(new_parent);
kobject_put(kobj);
kfree(devpath_string);
kfree(devpath);
return error;
}
/**
* kobject_del - unlink kobject from hierarchy.
* @kobj: object.
*/
void kobject_del(struct kobject *kobj)
{
if (!kobj)
return;
sysfs_remove_dir(kobj);
kobj->state_in_sysfs = 0;
kobj_kset_leave(kobj);
kobject_put(kobj->parent);
kobj->parent = NULL;
}
/**
* kobject_get - increment refcount for object.
* @kobj: object.
*/
struct kobject *kobject_get(struct kobject *kobj)
{
if (kobj)
kref_get(&kobj->kref);
return kobj;
}
/*
* kobject_cleanup - free kobject resources.
* @kobj: object to cleanup
*/
static void kobject_cleanup(struct kobject *kobj)
{
struct kobj_type *t = get_ktype(kobj);//获取ktype指针
const char *name = kobj->name;
pr_debug("kobject: '%s' (%p): %s\n",
kobject_name(kobj), kobj, __func__);
if (t && !t->release) /* 如果ktype存在,但ktype里的release不存在,则ktype没法释放,打印出告警信息 */
pr_debug("kobject: '%s' (%p): does not have a release() "
"function, it is broken and must be fixed.\n",
kobject_name(kobj), kobj);
/* send "remove" if the caller did not do it but sent "add" */
/* 如果没发remove却发了add的话,则向用户层发送remove, */
if (kobj->state_add_uevent_sent && !kobj->state_remove_uevent_sent) {
pr_debug("kobject: '%s' (%p): auto cleanup 'remove' event\n",
kobject_name(kobj), kobj);
kobject_uevent(kobj, KOBJ_REMOVE);
}
/* remove from sysfs if the caller did not do it */
if (kobj->state_in_sysfs) { /* 如果换处在sysfs中,则要先调用kobjcat_del,删除文件夹操作等 */
pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n",
kobject_name(kobj), kobj);
kobject_del(kobj);
}
if (t && t->release) { /* kobj_type和其释放函数都在的话,则调用release释放内存 */
pr_debug("kobject: '%s' (%p): calling ktype release\n",
kobject_name(kobj), kobj);
t->release(kobj);
}
/* free name if we allocated it */
if (name) {
pr_debug("kobject: '%s': free name\n", name);
kfree(name); /* 释放name占用的字节数 */
}
}
static void kobject_release(struct kref *kref)
{
kobject_cleanup(container_of(kref, struct kobject, kref));
}
/**
* kobject_put - decrement refcount for object.
* @kobj: object.
*
* Decrement the refcount, and if 0, call kobject_cleanup().
*/
void kobject_put(struct kobject *kobj)
{
if (kobj) {
if (!kobj->state_initialized)
WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "
"initialized, yet kobject_put() is being "
"called.\n", kobject_name(kobj), kobj);
kref_put(&kobj->kref, kobject_release);
}
}
static void dynamic_kobj_release(struct kobject *kobj)
{
pr_debug("kobject: (%p): %s\n", kobj, __func__);
kfree(kobj);
}
static struct kobj_type dynamic_kobj_ktype = {
.release = dynamic_kobj_release,
.sysfs_ops = &kobj_sysfs_ops,
};
/**
* kobject_create - create a struct kobject dynamically
*
* This function creates a kobject structure dynamically and sets it up
* to be a "dynamic" kobject with a default release function set up.
*
* If the kobject was not able to be created, NULL will be returned.
* The kobject structure returned from here must be cleaned up with a
* call to kobject_put() and not kfree(), as kobject_init() has
* already been called on this structure.
*/
struct kobject *kobject_create(void)//分配空间并内置了一个ktype,用于在计数为0时释放空间
{
struct kobject *kobj;
kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
if (!kobj)
return NULL;
kobject_init(kobj, &dynamic_kobj_ktype);//初始化kobj,并绑定ktype,注销函数
return kobj;
}
/**
* kobject_create_and_add - create a struct kobject dynamically and register it with sysfs
*
* @name: the name for the kset
* @parent: the parent kobject of this kobject, if any.
*
* This function creates a kobject structure dynamically and registers it
* with sysfs. When you are finished with this structure, call
* kobject_put() and the structure will be dynamically freed when
* it is no longer being used.
*
* If the kobject was not able to be created, NULL will be returned.
*/
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
struct kobject *kobj;
int retval;
kobj = kobject_create();
if (!kobj)
return NULL;
retval = kobject_add(kobj, parent, "%s", name);
if (retval) {
printk(KERN_WARNING "%s: kobject_add error: %d\n",
__func__, retval);
kobject_put(kobj);
kobj = NULL;
}
return kobj;
}
EXPORT_SYMBOL_GPL(kobject_create_and_add);
/**
* kset_init - initialize a kset for use
* @k: kset
*/
void kset_init(struct kset *k)//初始化kset
{
kobject_init_internal(&k->kobj);//处死话里面的kobj
INIT_LIST_HEAD(&k->list);//初始化kset链表
spin_lock_init(&k->list_lock);
}
/* 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;
}
struct sysfs_ops kobj_sysfs_ops = {
.show = kobj_attr_show,
.store = kobj_attr_store,
};
/**
* kset_register - initialize and add a kset.
* @k: kset.
*/
int kset_register(struct kset *k)
{
int err;
if (!k)
return -EINVAL;
kset_init(k);//初始化kset
err = kobject_add_internal(&k->kobj);//将kset的kobj添加到kernel(创建一个文件夹)
if (err)
return err;
kobject_uevent(&k->kobj, KOBJ_ADD);
return 0;
}
/**
* kset_unregister - remove a kset.
* @k: kset.
*/
void kset_unregister(struct kset *k)
{
if (!k)
return;
kobject_put(&k->kobj);//引用数减1,直到减为0,调用release函数销毁该kobject
}
/**
* kset_find_obj - search for object in kset.
* @kset: kset we're looking in.
* @name: object's name.
*
* Lock kset via @kset->subsys, and iterate over @kset->list,
* looking for a matching kobject. If matching object is found
* take a reference and return the object.
*/
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(k);
break;
}
}
spin_unlock(&kset->list_lock);
return ret;
}
static void kset_release(struct kobject *kobj)
{
struct kset *kset = container_of(kobj, struct kset, kobj);
pr_debug("kobject: '%s' (%p): %s\n",
kobject_name(kobj), kobj, __func__);
kfree(kset);
}
static struct kobj_type kset_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.release = kset_release,
};
/**
* kset_create - create a struct kset dynamically
*
* @name: the name for the kset
* @uevent_ops: a struct kset_uevent_ops for the kset
* @parent_kobj: the parent kobject of this kset, if any.
*
* This function creates a kset structure dynamically. This structure can
* then be registered with the system and show up in sysfs with a call to
* kset_register(). When you are finished with this structure, if
* kset_register() has been called, call kset_unregister() and the
* structure will be dynamically freed when it is no longer being used.
*
* If the kset was not able to be created, NULL will be returned.
*/
static struct kset *kset_create(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)//分配一个kset空间,并定义一个kset_ktype类型的ktype,用于释放所有由它分配的kset空间
{
struct kset *kset;
int retval;
kset = kzalloc(sizeof(*kset), GFP_KERNEL);
if (!kset)
return NULL;
retval = kobject_set_name(&kset->kobj, 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;//给kobj里面的ktype绑定一个release函数
kset->kobj.kset = NULL;
return kset;
}
/**
* kset_create_and_add - create a struct kset dynamically and add it to sysfs
*
* @name: the name for the kset
* @uevent_ops: a struct kset_uevent_ops for the kset
* @parent_kobj: the parent kobject of this kset, if any.
*
* This function creates a kset structure dynamically and registers it
* with sysfs. When you are finished with this structure, call
* kset_unregister() and the structure will be dynamically freed when it
* is no longer being used.
*
* If the kset was not able to be created, NULL will be returned.
*/
struct kset *kset_create_and_add(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)//动态创建一个kset,并调用kset_register将其注册到kernel
{
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;
}
EXPORT_SYMBOL_GPL(kset_create_and_add);
EXPORT_SYMBOL(kobject_get);
EXPORT_SYMBOL(kobject_put);
EXPORT_SYMBOL(kobject_del);
EXPORT_SYMBOL(kset_register);
EXPORT_SYMBOL(kset_unregister);