get_gendisk() 手続き

container_of を使ってる手続き。container_of マクロの確認を昨晩したんですが、なんとなく微妙な違和感。とりあえず 3rd Edition のデバイス管理云々な部分を参照しつつ、色々確認。
get_gendisk() の中身的には以下なカンジのはず。

  • kobj_lookup() で kobj なオブジェクトを取得
    • 3rd Edition によれば kobj_lookup() は_k オブジェクトマッピングドメインとデバイス番号を入力引数として受け取_って、_ハッシュテーブルを検索_とありますが、これがおそらく struct kobj_map な bdev_map (上記の k オブジェクトマッピングドメイン) と思われます。
    • で、3rd Edition の記述から類推するに (上記で引用しているのはキャラクタデバイスなドライバの記述なので)、渡されたデバイス番号を所有する block_device ディスクリプタの k オブジェクト を戻す、と思ってて良いのでしょうか。
    • む、block_device ディスクリプタには kobject なメンバは無いな。でも gendisk なメンバはある。このあたりは kobj_lookup 見てみるしかないのか
  • 戻された kobject が NULL じゃなかったらその kobject がある gendisk オブジェクトの先頭アドレスを戻す

という事になるんか。完全に前提が

  • kobj_lookup() で戻ってくるのは gendisk オブジェクトの中にある kobject オブジェクトのアドレス
    • そうでないと container_of は使えない
  • 上記の gendisk 構造体は block_device ディスクリプタの gendisk な属性

になっている事が類推できる。なんか凄く都合が良い話の展開だなぁ、と自分でも思います。

kojb_map 構造体

定義は drivers/base/map.c で以下。

struct kobj_map {
	struct probe {
		struct probe *next;
		dev_t dev;
		unsigned long range;
		struct module *owner;
		kobj_probe_t *get;
		int (*lock)(dev_t, void *);
		void *data;
	} *probes[255];
	struct mutex *lock;
};

kobj_lookup() 手続きも抽象化されてます。

  • probe って関数ポインタ定義して
		struct kobject *(*probe)(dev_t, int *, void *);
  • ハッシュチェインを手繰りつつ
  • get 属性を probe に代入しといて
		probe = p->get;
  • 呼び出し
		kobj = probe(dev, index, data);
  • 上記の戻りが NULL でなければそれを戻している
    • NULL だったら、な処理も微妙ですが ...

3rd Edition によると add_disk という手続きがポイントなようですが。って get_gendisk() 手続きが定義されてる block/genhd.c に add_disk() という手続きが定義されとりますな。確認してみます。

add_disk() 手続き

この手続きは 3rd Edition の 14.4.2.8 に詳細な説明あり。block/genhd.c 内で以下な定義。

/**
 * add_disk - add partitioning information to kernel list
 * @disk: per-device partitioning information
 *
 * This function registers the partitioning information in @disk
 * with the kernel.
 */
void add_disk(struct gendisk *disk)
{
	disk->flags |= GENHD_FL_UP;
	blk_register_region(MKDEV(disk->major, disk->first_minor),
			    disk->minors, NULL, exact_match, exact_lock, disk);
	register_disk(disk);
	blk_register_queue(disk);
}

なんとなく説明と合致してはいるのですが、blk_register_region() を掘ってみると同じソースファイルにて以下な記述。

/*
 * Register device numbers dev..(dev+range-1)
 * range must be nonzero
 * The hash chain is sorted on range, so that subranges can override.
 */
void blk_register_region(dev_t dev, unsigned long range, struct module *module,
			 struct kobject *(*probe)(dev_t, int *, void *),
			 int (*lock)(dev_t, void *), void *data)
{
	kobj_map(bdev_map, dev, range, module, probe, lock, data);
}

probe に NULL 渡してるし orz
あ、違うや。exact_match というのがソレに当たりますな。良かった。exact_match も block/genhd.c で以下な定義。

static struct kobject *exact_match(dev_t dev, int *part, void *data)
{
	struct gendisk *p = data;
	return &p->kobj;
}

あ、成程ね。

  • add_disk() 手続きから blk_register_region() 手続きに渡すラストの引数は gendisk なオブジェクト
  • blk_register_region() 手続きにて kobj_map() 手続きに渡すラストの引数は上記で渡された gendisk オブジェクトを渡す形になっている
  • kobj_map() 手続きでは上記の gendisk オブジェクトを bdev_map の data属性にセット
  • kojb_lookup() から呼ばれる get 属性に定義された手続き exact_match は引数 data を gendisk 構造体と見て kobj 属性を取り出す

という事で話が繋がりました。しかし繋り方が複雑ですな。