ksetの実装


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の場合、独自の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;
}

追記

/procおよび/sysも、カーネル管理下の情報をユーザにエクスポートする目的で機能その物は同じです。/sysはkobjをカテゴリ/階層的に管理できるだけでなく、掛かるコールバックを自由に設定できるようになっています。ゆえに実装も複雑となっているのですが。単に読み書きのカーネル管理下の静的な情報は、/procで十分と言える訳で、/procおよび/sys両方とも実装されている由縁ではと思います。


最終更新 2014/10/17 04:04:23 - north
(2014/10/15 19:06:33 作成)


検索

アクセス数
3689338
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。