ksetの実装
kset_create_and_add()でksetを作成しsysに登録します。nameがksetの名称で、parent_kobjの配下にnameのディレクトリが作成されます。uevent_opsはkset配下のオブジェクトで、kobject_actionのアクションがあった場合のコールバック関数となります。必要なければNULLです。
bus/module/driver等、かかる多々のkobjを有するkobjの場合、新規kobjを登録時、重複していないかのチェックが必要で、ksetを走査することができるように、kset_find_obj()が実装されています。
kset_ktypeはksetに属性が設定されている場合のみ有効となり、その属性にコールバック関数が設定されているなら、それをコールするだけです。
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の場合、独自のkypeを定義し、それをkobj.ktypeに設定してkobject_add_internal()を直接コールします。
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のコンテナと言うことです。
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/.storeが、ディレクトリの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; }