ちょっと再開してみる

drivers/net/8139too.c を開いて中身をほじくってみる。微妙なままのメモを。

エントリポイントから

Linux カーネル解析入門では_コツとして、「エントリポイントから読んでいく」_とありますので、そこから。

static int __init rtl8139_init_module (void)
{
       /* when we're a module, we always print a version message,
        * even if no 8139 board is found.
        */
#ifdef MODULE
       printk (KERN_INFO RTL8139_DRIVER_NAME "\n");
#endif

       return pci_module_init (&rtl8139_pci_driver);
}

pci_module_init の上のカーソル置いて、M-. すると include/linux/pci.h の以下の位置にジャンプ。

/*
 * pci_module_init is obsolete, this stays here till we fix up all usages of it
 * in the tree.
 */
#define pci_module_init pci_register_driver

本にもある通り、あるいはコメントにも obsolete とある。実体は pci_register_driver ッスか。で、さらに M-. してみると以下に位置に。

static inline int pci_register_driver(struct pci_driver *drv) { return 0;}

なにー??と言いつつ上にめくってみると

#ifndef CONFIG_PCI

で括られている。がしかし、実体はいずこに、と言いつつソースツリーを grep すると、drivers/pci/pci-driver.c に pci_register_driver 関数が定義されているの発見。これで良いのかなぁ。関数の先頭にあるコメントが以下。

/**
 * pci_register_driver - register a new pci driver
 * @drv: the driver structure to register
 *
 * Adds the driver structure to the list of registered drivers.
 * Returns a negative value on error, otherwise 0.
 * If no error occurred, the driver remains registered even if
 * no device was claimed during registration.
 */

なんとなく合ってそうな感じ。コメントによると登録済みドライバのリストにドライバ構造体を追加、とある。あるいはエラーなら負の値を戻し、そうでなければ 0 と。最後のソレが微妙。
で、8139too のエントリポイントから渡されている構造体はどうなっているか、というと。本の p.253 にある構造体か。

static struct pci_driver rtl8139_pci_driver = {
       .name           = DRV_NAME,
       .id_table       = rtl8139_pci_tbl,
       .probe          = rtl8139_init_one,
       .remove         = __devexit_p(rtl8139_remove_one),
#ifdef CONFIG_PM
       .suspend        = rtl8139_suspend,
       .resume         = rtl8139_resume,
#endif /* CONFIG_PM */
};

struct pci_driver 型

を、これって gcc 拡張な構造体の初期化ってヤツだな。struct pci_driver 型も include/linux/pci.h にて定義されている。長いかと思ったら以外に短いんで以下に引用。

struct pci_driver {
       struct list_head node;
       char *name;
       struct module *owner;
       const struct pci_device_id *id_table;   /* must be non-NULL for probe to be called */
       int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id);   /* New device inserted */
       void (*remove) (struct pci_dev *dev);   /* Device removed (NULL if not a hot-plug capable driver) */
       int  (*suspend) (struct pci_dev *dev, pm_message_t state);      /* Device
suspended */
       int  (*resume) (struct pci_dev *dev);                   /* Device woken up */
       int  (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable);   /* Enable wake event */
       void (*shutdown) (struct pci_dev *dev);

       struct device_driver    driver;
       struct pci_dynids dynids;
};

で、pci_register_driver 関数の中身をざっくり見るに構造体メンバの driver のメンバを設定して、driver_register 関数を driver を渡して呼び出している模様。途中で

       pci_init_dynids(&drv->dynids);

みたいなコトをやってますな。driver_register 関数呼び出しの直前。渡しているのは struct dynids 型の dynids という名前のメンバになっている。まず、struct dynids 型が定義されているのは include/linux/pci.h です。

struct pci_dynids {
       spinlock_t lock;            /* protects list, index */
       struct list_head list;      /* for IDs added at runtime */
       unsigned int use_driver_data:1; /* pci_driver->driver_data is used */
};

リストっぽいソレがありますな。定義は include/linux/list.h で以下。

struct list_head {
       struct list_head *next, *prev;
};

ここには線形リストを操作 (というか初期化か) するマクロがいくつか定義されている様子。上記の定義の直下にあります。

#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
       struct list_head name = LIST_HEAD_INIT(name)

#define INIT_LIST_HEAD(ptr) do { \
       (ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)

で、pci_init_dynids 関数は同じ drivers/pci/pci-driver.c に定義されている。

static inline void
pci_init_dynids(struct pci_dynids *dynids)
{
       spin_lock_init(&dynids->lock);
       INIT_LIST_HEAD(&dynids->list);
}

どっちも初期化してる感じですな。特にマクロの方はとりあえず指しとけ感満点。おそらく静的な領域なんで 0 で初期化されてるから、というのが理由ではないかと。

driver_register 関数

を見る前に struct device_driver 構造体の定義を以下に。定義されているのは include/linux/device.h

struct device_driver {
       char                    * name;
       struct bus_type         * bus;

       struct completion       unloaded;
       struct kobject          kobj;
       struct list_head        devices;

       struct module           * owner;

       int     (*probe)        (struct device * dev);
       int     (*remove)       (struct device * dev);
       void    (*shutdown)     (struct device * dev);
       int     (*suspend)      (struct device * dev, pm_message_t state, u32 level);
       int     (*resume)       (struct device * dev, u32 level);
};

で、driver_register 関数ですが、ここでも INIT_LIST_HEAD してるな。ちょっと struct pci_driver 構造体とそのメンバの dynids (struct pci_dynids 型) と driver (struct device_driver 型) の関係が現時点で分からんだけに微妙。

int driver_register(struct device_driver * drv)
{
       INIT_LIST_HEAD(&drv->devices);
       init_completion(&drv->unloaded);
       return bus_add_driver(drv);
}

ここも init_* なソレはスルーで最後のソレを見てみた方が良さげ。
# ってか、init_completion は etags ではヒモが付いてなかった。

で、bus_add_driver 関数は drivers/base/bus.c にて定義されている模様。で関数定義の直上に記述されているのが以下のコメントなんですが

/**
 *      bus_add_driver - Add a driver to the bus.
 *      @drv:   driver.
 *
 */

driver を bus に追加 ... って bus って何よ??

とりあえず手続きの先頭でこんなコトをしている。

       struct bus_type * bus = get_bus(drv->bus);

ちなみに drv->bus は pci_register_driver 関数にて以下のように初期化されている。

       drv->driver.bus = &pci_bus_type;

まず

  • pci_bus_type って何だ
  • struct bus_type 型は何のためのものか
  • get_bus って何してるのか

が (以下略

順番にチェック。まず pci_bus_type という変数は pci_driver.c にて定義。これは静的変数ですな。この変数 EXPORT_SYMBOL ってマクロで何かしているようなんですが、何だろうか。(気になるソレが沢山あるんですが、とりあえずスルー)
# export されるべき、って bus.txt に書いてある

struct bus_type pci_bus_type = {
       .name           = "pci",
       .match          = pci_bus_match,
       .hotplug        = pci_hotplug,
       .suspend        = pci_device_suspend,
       .resume         = pci_device_resume,
       .dev_attrs      = pci_dev_attrs,
};

ちなみに struct bus_type 型は include/linux/device.h にて以下の定義。

struct bus_type {
       char                    * name;

       struct subsystem        subsys;
       struct kset             drivers;
       struct kset             devices;

       struct bus_attribute    * bus_attrs;
       struct device_attribute * dev_attrs;
       struct driver_attribute * drv_attrs;

       int             (*match)(struct device * dev, struct device_driver * drv);
       int             (*hotplug) (struct device *dev, char **envp,
                                   int num_envp, char *buffer, int buffer_size);
       int             (*suspend)(struct device * dev, pm_message_t state);
       int             (*resume)(struct device * dev);
};

うーん。だんだんドロ沼にハマってる感じがする。とほほ、言いつつ google 先生にお伺いを立ててみると、Documentation/driver-model/ 配下に文書があるような記述を発見。今からチェックしてみる。ってか構造体メンバのソレを把握できてないだけど大丈夫なのか。< 自分

ちょい整理

イキオイだけでヤッツケちゃってるんでこーゆー事になる。構造体の関係なんかをちょっと整理してみると

  • struct pci_driver 型は
    • struct device_driver 型のメンバ (driver) を持っている
    • struce pci_dynids 型のメンバ (dynids) を持っている
    • pci_register_driver 関数に渡される
    • 静的に領域確保
  • struct pci_driver 型のメンバの struct dynids 型は
    • pci_register_driver 関数の中で pci_init_dynids に渡され初期化
  • struct pci_driver 型のメンバの struct device_driver 型は
    • pci_register_driver 関数の中でいくつかのメンバに値が設定
    • その後、register_driver 関数に渡される
    • struct bus_type 型のポインタのメンバ (bus) を持っている
    • register_driver 内でさらに bus_add_driver 関数に渡される
  • 上記の構造体はいずれも struct list_head 型のメンバを持っている

というのがソースから見える。なんかワケワカ。一応、Documentation/drivre-model/driver.txt が参考にはなるんですが、英語が微妙。

bus に戻る

ってか戻れない。get_bus 関数の実装見たんですがすげぇ微妙なマクロ使っとる。とりあえず container_of マクロのコメントのみ以下に。

/**
 * container_of - cast a member of a structure out to the containing structure
 *
 * @ptr:	the pointer to the member.
 * @type:	the type of the container struct this is embedded in.
 * @member:	the name of the member within the struct.
 *
 */

もう少し時間に余裕があれば、なんですが言い訳は止め。あと Documentation/driver-model/bus.txt にもう少しきちんと目を通しておきたい。

同時にこのあたりのソレを LKH-jp 方面に feedback してみたい。ってか明日にでも着手。今見てるのは 2.6.12 なんで別途 2.6.10 オトして用意しとく必要あり、だな。