ksetの実装
Rev.1を表示中。最新版はこちら。
kset_create_and_add()でksetを作成しsysに登録します。nameがksetの名称で、parent_kobjの配下にnameのディレクトリが作成されます。uevent_opsはkset配下のオブジェクトで、kobject_actionのアクションがあった場合のコールバック関数となります。必要なければNULLです。
enum kobject_action {
KOBJ_ADD,
KOBJ_REMOVE,
KOBJ_CHANGE,
KOBJ_MOVE,
KOBJ_ONLINE,
KOBJ_OFFLINE,
KOBJ_MAX
};
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;
}
EXPORT_SYMBOL_GPL(kset_create_and_add);
kset構造体にkset名称(kset->kobj->name)、親オブジェクト(kset->kobj.parent)、uevent/ktypeコールバックを設定します。kset_ktypeは備考参照。なお通常のkobjeの場合ktypeは独自の実装となります。
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, name);
if (retval) {
kfree(kset);
return NULL;
}
kset->uevent_ops = uevent_ops;
kset->kobj.parent = parent_kobj;
kset->kobj.ktype = &kset_ktype;
kset->kobj.kset = NULL;
return kset;
}
ksetをkset->kobj.parentにkset->kobj.nameでディレクトリを作成します。kobject_add_internal()はkset登録に特化した関数でなく、全てのkobjを登録する時に使われます。ksetは単にkobjの1つと言うことです。
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;
}
kobj->ksetにksetが設定されているなら、kobj->kset->kobj->parentを、そうでないなら設定するkobj->parentを親とします。kobj->parent/kobj->ksetが設定されていないなら、/sys直下に登録されます。ksetに登録される場合、kobj_kset_join()でkset->listにkobjをリストし、create_dir()で/sysにディレクトリ(オブジェクト)/ファイル(属性)が作成されます。
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);
if (kobj->kset) {
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj);
kobj->parent = parent;
}
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);
if (error) {
kobj_kset_leave(kobj);
kobject_put(parent);
kobj->parent = NULL;
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をkset->listにリストします。同時にkset_get()でksetの参照カウンタをインクリメントすることで、リリースされないようにします。ksetをリリースするには、この参照カウンタをデクリメントし、0になるとstruct kobj_typeのreleaseがコールされます。
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);
spin_unlock(&kobj->kset->list_lock);
}
sysfs_create_dir()でディレクトリ(オブジェクト)を、populate_dir()でファイル(属性)を作成します。
static int create_dir(struct kobject *kobj)
{
int error = 0;
if (kobject_name(kobj)) {
error = sysfs_create_dir(kobj);
if (!error) {
error = populate_dir(kobj);
if (error)
sysfs_remove_dir(kobj);
}
}
return error;
}
ファイル(属性)を作成します。kobj->ktype->default_attrs[].nameをファイル名、kobj->ktype->default_attrs[].modeをファイル属性とし、/sys配下のファイルを作成します。
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;
}
ファイル(属性)を読み書きすると、/sysfsのコールバック関数として、struct kobj_type->sysfs_opsの.show/.releaseが、ディレクトリのkobjを引数としてコールされます。属性毎に独自のコールバック関数を定義したいなら、kobjからattributeを取得し、そこに独自のコールバック関数を定義することで実装できるようになっています。補足
kobj作成後に、動的に属性を追加する処理として、sysfs_create_file()というのが実装されています。(kobjectを追加毎にかかる設定値用に独自の属性が必要なケース)、しかし、本関数で作成されたファイルの読み書きのコールバック関数は、ディレクトリのkobjのktypeの同じ関数がコールされます。従ってsysfs_create_file()で後に属性を追加するkobjectの場合、ktypeのコールバック関数はそのような物として実装する必要があります。bus/module/driver等、かかる多々のkobjを有するkobjの場合、新規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(k);
break;
}
}
spin_unlock(&kset->list_lock);
return ret;
}
kset_ktypeはksetに属性が設定されている場合のみ有効となり、その属性にコールバック関数が設定されているなら、それをコールするだけです。
static struct kobj_type kset_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.release = kset_release,
};
const struct sysfs_ops kobj_sysfs_ops = {
.show = kobj_attr_show,
.store = 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)
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;
}




