MIRACLE
メールサービス申込 ユーザー登録 パートナー情報
お問い合わせ FAQ サイトマップ
MIRACLE LINUXの特長 製品紹介 サービス案内 購入 サポート 技術フォーラム

« dump解析(1) 一の巻 | メイン | ログイン時のサウンド »

2006年7月19日 (水)

dump解析(1) 二の巻

さて、前回、buffer_headを特定したものの、なぜこのbuffer_headがロックされたままとなっているのか分かりません。
さらに調べを進めていきたいと思います。

__wait_on_buffer()でなぜ待ちつづけるかというと、__wait_on_buffer()内のsync_buffer()でバッファのロックが解除されないからです。
では、sync_buffer()を調べてみましょう。
-----
fs/buffer.c
static void sync_buffer(struct buffer_head *bh)
{
        struct block_device *bd;

        smp_mb();
        bd = bh->b_bdev;
        if (bd)
                blk_run_address_space(bd->bd_inode->i_mapping);
}
-----

ここで、b_bdevは、前回、0xf7f10a80が入っていましたので、blk_run_address_space()が実行されるはずです。そこで、bd->bd_inode->i_mappingを調べてみます。
----
crash> struct block_device 0xf7f10a80
struct block_device {
  bd_dev = 9437185,
  bd_inode = 0xf7f10af4,
  bd_openers = 1,
-----
さらにbd_inodeを調べます。
------
crash> struct inode 0xf7f10af4
struct inode {
  i_hash = {
    next = 0x0,
    pprev = 0xf54527f4
  },
...略 ...
  i_op = 0xc046cae0,
  i_fop = 0xc046cb40,
  i_sb = 0xf7f47200,
  i_flock = 0x0,
  i_mapping = 0xf7f10ba4,
  i_data = {
    host = 0xf7f10af4,
    page_tree = {
      height = 4,
      gfp_mask = 544,
      rnode = 0xf620be38
    },
--------

i_mapping=0xf7f10ba4が入っていますので、さらに進んで
-----
static inline void blk_run_address_space(struct address_space *mapping)
{
        if (mapping)
                blk_run_backing_dev(mapping->backing_dev_info, NULL);
}
------
ということになります。mapping->backing_dev_infoを調べてみます。
-----
crash> struct address_space 0xf7f10ba4
  host = 0xf7f10af4,
...
backing_dev_info = 0xf74acec8,
...
-----
このbacking_dev_infoを使うのは、blk_run_backing_dev()です。
-----
static inline void blk_run_backing_dev(struct backing_dev_info *bdi,
                                       struct page *page)
{
        if (bdi && bdi->unplug_io_fn)
                bdi->unplug_io_fn(bdi, page);
}
----
どこまでも地道な作業が進みます...。
----
crash> struct backing_dev_info 0xf74acec8
struct backing_dev_info {
  ra_pages = 32,
  state = 0,
  memory_backed = 0,
  congested_fn = 0,
  congested_data = 0x0,
  unplug_io_fn = 0xc022446f <blk_backing_dev_unplug>,
  unplug_io_data = 0xf74acd80
}
-----

ようやくデバイスのリクエストキューの部分まで来ました。ちなみにここで、*pageはNULLです。
------
static void blk_backing_dev_unplug(struct backing_dev_info *bdi,

                                   struct page *page)
{
        request_queue_t *q = bdi->unplug_io_data;

        /*
         * devices don't necessarily have an ->unplug_fn defined
         */
        if (q->unplug_fn)
                q->unplug_fn(q);
}
------

上記関数内の

-----
request_queue_t *q = bdi->unplug_io_data;

-----

これがI/Oリクエストキューであることが判明しました。

----
  unplug_io_data = 0xf74acd80

-----

それならば、リクエストキューの中身を確認します。

-----
crash> struct request_queue_t 0xf74acd80
struct request_queue_t No struct type named request_queue_t.
----
エ、エラーでした。
気を取り直して、
-----
$ cd include/linux
$ grep request_queue_t *
blkdev.h:typedef struct request_queue request_queue_t;
-----
ということで、request_queue型が正しいようなので、改めて、
-----
crash> struct request_queue 0xf74acd80
struct request_queue {
  queue_head = {
    next = 0x0,
    prev = 0x0
  },
  lasstruct request_queue {
  queue_head = {
    next = 0x0,
    prev = 0x0
  },
  last_merge = 0x0,
  elevator = {
t_merge = 0x0,
  elevator = {
... 略 ...
  request_fn = 0,
  back_merge_fn = 0,
  front_merge_fn = 0,
  merge_requests_fn = 0,
  make_request_fn = 0xf883992e,
  prep_rq_fn = 0,
  unplug_fn = 0xf88396ef,
  merge_bvec_fn = 0,
  activity_fn = 0,
  issue_flush_fn = 0xc026e939 <md_flush_all>,
  unplug_timer = {
    entry = {
      next = 0x0,
      prev = 0x0
    },
    expires = 0,
    lock = {
      lock = 1,
      magic = 3735899821
    },
    magic = 1267182958,
    function = 0xc0224488 <blk_unplug_timeout>,
    data = 4148874624,
    base = 0x0
  },
  unplug_thresh = 4,
  unplug_delay = 3,
  unplug_work = {
    pending = 0,
    entry = {
      next = 0xf74ace90,
      prev = 0xf74ace90
    },
    func = 0xc022447f <blk_unplug_work>,
...以下略。
-------
となっています。
-----
  unplug_fn = 0xf88396ef
-----
が設定されているので、
------
        if (q->unplug_fn)
                q->unplug_fn(q);
------
は実行されているはずです。
------
crash> sym 0xf88396ef
f88396ef (t) raid1_unplug
------

これでようやくsync_buffer()したときに、今対象としているbuffer_headに対しては、raid1_unplug()という処理が行われるんだなぁ〜というところまでたどり着きました。

------
static void raid1_unplug(request_queue_t *q)
{
        unplug_slaves(q->queuedata);
}
----
q->queuedataが必要みたいなので、再度request_queueの中身に戻って、
----
queuedata = 0xf7f2e400,
----
を得ます。

unplug_slaves()は、各RAIDレベルごとに用意されている関数なので、raid1.cのunplug_slaves()を確認します。

-----

static void unplug_slaves(mddev_t *mddev)
{
        conf_t *conf = mddev_to_conf(mddev);
        int i;
        unsigned long flags;

        spin_lock_irqsave(&conf->device_lock, flags);
        for (i=0; i<mddev->raid_disks; i++) {
                mdk_rdev_t *rdev = conf->mirrors[i].rdev;
                if (rdev && atomic_read(&rdev->nr_pending)) {
                        request_queue_t *r_queue = bdev_get_queue(rdev->bdev);

                        atomic_inc(&rdev->nr_pending);
                        spin_unlock_irqrestore(&conf->device_lock, flags);

                        if (r_queue->unplug_fn)
                                r_queue->unplug_fn(r_queue);

                        spin_lock_irqsave(&conf->device_lock, flags);
                        rdev_dec_pending(rdev, mddev);
                }
        }
        spin_unlock_irqrestore(&conf->device_lock, flags);
}
-------

まず、*confを導くために、mddev_to_conf()を確認してみます。

------
typedef struct r1_private_data_s conf_t;

#define mddev_to_conf(mddev) ((conf_t *) mddev->private)
------

ということなので、

------
crash> struct conf_t 0xf7f2e400
struct: cannot handle "conf_t": try "gdb whatis" or "gdb ptype"

------

そういえば、さきほどconf_tはtypedefされていたので...

-----
crash> struct r1_private_data_s 0xf7f2e400
struct: cannot handle "r1_private_data_s": try "gdb whatis" or "gdb ptype"
------
まだだめです。そういえば、raid1.koをロードしていないので、structが判別できないんですね。

-----
crash> mod
MODULE   NAME            SIZE  OBJECT FILE
f8825e80  sd_mod         20480  (not loaded)  [CONFIG_KALLSYMS]
f883cb00  raid1          19584  (not loaded)  [CONFIG_KALLSYMS]
.....

-----

そこで、raid1.koをロードします。

-----

crash> mod -s raid1 raid1.ko
MODULE   NAME            SIZE  OBJECT FILE
f883cb00  raid1          19584  raid1.ko
-----

再度、confを調べます。
-----
crash> struct r1_private_data_s 0xf7f2e400
struct: cannot handle "r1_private_data_s": try "gdb whatis" or "gdb ptype"
-----

やっぱりだめです。

注: これは実はkernel-debuginfoに含まれるraid1.ko.debugをロードすれば問題なくstructコマンドで解析できることに後で気づきました。

とにかく、うまくいかなかったので、

----
crash> rd 0xf7f2e400 100
f7f2e400:  f7e5d800 f883ca80 00900001 00000001   ................
f7f2e410:  f7e5d880 f7e5d880 00000000 00000000   ................

-----

*mddev = f7e5d800 ということがメモリダンプから分かります。


include/linux/raid/md_k.hから

-----
typedef struct mddev_s mddev_t;
-----

となっているので、

-----
crash> struct mddev_s f7e5d800

struct mddev_s {
  private = 0xf7f2e400,
  pers = 0xf74a2f80,
  unit = 2,
  md_minor = 1,

-----

*conf = 0xf7f2e400 ということが分かります。

さらに次のルーチンで、raidのディスクごとの処理をしているので、raid_disksの

数値を探します。

-----
        for (i=0; i<mddev->raid_disks; i++) {
-----

すると、
-----
  layout = -135931836,
  raid_disks = -135931836,
  max_disks = 1,
-----

と、異常な数値になっています。

その結果、raid_disksの数値が負のため、各ディスクに対するI/O発行処理のループが実行されず、バッファのステータスが変わらなかったために、ストールが発生したと思われます。

最後は非常にあっさりしたものでしたが、そこまでにたどり着くまでは非常に地道にデータとソースを確認する作業が続くのでした。

トラックバック

このページのトラックバックURL:
http://www.typepad.jp/t/trackback/4447/6428780

このページへのトラックバック一覧 dump解析(1) 二の巻:

コメント

コメントを投稿

コメントは記事の投稿者が承認するまで表示されません。

会社情報 採用情報 個人情報保護方針 商標等取り扱い事項 English
Copyright(c)2000-2006 MIRACLE LINUX CORPORATION. All Rights Reserved.