kernel fcache-bug

20
ファイルキャッシュクリアの謎 技術Bar#9 2012/12/4 光成滋生

Upload: mitsunari-shigeo

Post on 24-May-2015

1.559 views

Category:

Technology


4 download

DESCRIPTION

how to find a bug in kernel/fs about wrong page cache flush in resizing MD device.

TRANSCRIPT

Page 1: Kernel fcache-bug

ファイルキャッシュクリアの謎

技術Bar#9

2012/12/4 光成滋生

Page 2: Kernel fcache-bug

cybozu.comで障害発生

/ 20 2

ストレージ容量拡張操作の結果、Officeが動いているLinuxマシンのページキャッシュが約15分おきに破棄されるという未知の現象が発生したためです

Page 3: Kernel fcache-bug

現象の詳細

mdadmでLVMのパーティションを拡大すると15分毎にpage cacheがクリアされる

犯人はだれか?

/ 20 3

hard disk

kernel/device driver

LVM

mdadm

ext3

MySQLなどのアプリ

パーティション管理

ソフトウェアRAID

ファイルシステム

メモリにキャッシュ

これがクリアされて激遅に

Page 4: Kernel fcache-bug

調査のやりかた

現象の再現方法の構築

頻度が低かったり環境によって起こらないと面倒

原因を推測して絞り込み

トライアンドエラー

さまざまなツールを使う

特定したら修正して確認

はずれていたらまた別の原因を推測

/ 20 4

Page 5: Kernel fcache-bug

現象の確認

hazamaでの容量の変更は500GB→1TB

バックグラウンドリサイズが長時間

数十MB程度では17分以内に終わってしまう

手元の環境でディスクを用意して…

1時間待ったが発現しなかった

私の勘違い

キャッシュがクリアされるのはそのディスクに構築したファイルシステム上のもののみ

再度テスト

クリアされることを確認

約17分=>概ね1000秒であることも判明

/ 20 5

Page 6: Kernel fcache-bug

犯人は誰か

全然見当がつかない

普通ユーザラウンドで明示的に破棄なんてしない

page cacheを破棄しそうなkernelの関数を列挙

flush_disk, invalidate_disk, invalidate_parition, ...

これらの関数を呼び出すやつを探そう

ftraceを使って探す

ubuntuでは標準搭載

呼ばれた関数,stacktrace,eventなどがわかる

/sys/kernel/debug/tracingに様々な値を書き込むことでパラメータを設定する

結構複雑で慣れるまで難しかった

/ 20 6

Page 7: Kernel fcache-bug

ftraceの例

ext4系関数が呼ばれたときのcall treeを表示

ext4のファイルシステムをmoutすると関連関数のlogが表示される

/ 20 7

// debugfsをmount

mount -t debugfs debugfs /sys/kernel/debug

// stack tracerを有効

echo 1 > /proc/sys/kernel/stack_tracer_enabled

cd /sys/kernel/debug/tracing

// ext4で始まる関数名をターゲットに

echo "ext4*" > set_ftrace_filter

// 関数トレーサーをセット

echo function > current_tracer

echo 0 > trace cat trace_pipe

Page 8: Kernel fcache-bug

あたりをつける

ディスクを交換したり,resize開始直後に呼ばれる関数をセットして17分待つ

呼ばれない…

別の関数を試すことの繰り返し

このあたりが結構辛い

/ 20 8

Page 9: Kernel fcache-bug

漸くヒット

ここには重要な情報が… / 20 9

__invalidate_deviceが網にかかった!

mdadm-900 [001] .... 75491.384221: __invalidate_device <-invalidate_partition

mdadm-900 [001] .... 75491.384235: <stack trace>

=> ftrace_call

=> invalidate_partition

=> rescan_partitions

=> __blkdev_get

=> blkdev_get

=> blkdev_open

=> do_dentry_open

=> nameidata_to_filp

=> do_last

=> path_openat

=> do_filp_open

=> do_sys_open

=> sys_open => system_call_fastpath

下の関数が上の関数を呼び出している

ここから下はユーザ空間

Page 10: Kernel fcache-bug

犯人はmdadmのdaemon?

ユーザ空間側なのでgdbでひっかけられる

sudo gdb –p <processId>でatach

backtraceしてみる

時間の間隔も正確に1000秒なのがわかる / 20 10

__invalidate_deviceが網にかかった!

mdadm-900 [001] .... 75491.384221: __invalidate_device <-invalidate_partition

mdadm-900 [001] .... 75491.384235: <stack trace>

=> ftrace_call ...

gdb) bt

#0 0x00007f85b1ebf103 in __select_nocancel ()

at ../sysdeps/unix/syscall-template.S:82

#1 0x000000000040de66 in mdstat_wait (seconds=1000) at mdstat.c:317 #2 0x000000000042dea1 in Monitor (devlist=0x0, mailaddr=0x1a7b020 "root",

Page 11: Kernel fcache-bug

位置関係

/ 20 11

hard disk

md driver

monitor daemon

invalidate_partition disk driver

md daemon

clear page cache

use space

kernel ?

Page 12: Kernel fcache-bug

mdadmのソースをで追いかける

まずこれで1000秒を30秒に短縮できる

調査効率が大幅に改善される

しかしやや戸惑いが

monitorコマンドがクリアするとも思えない

ファイルキャッシュがクリアされる関数が何か確認しながらgdbでstep実行

daemon内部で/dev/mdをopenするだけでクリアされることが判明

mdのdevice driverの問題?

/ 20 12

fd = open("dev/md/test..", O_RDONLY);

Page 13: Kernel fcache-bug

位置関係

/ 20 13

hard disk

md driver

monitor daemon

invalidate_partition disk driver

md daemon

use space

kernel

md_open

clear page cache

?

Page 14: Kernel fcache-bug

md_openを追いかける

check_disk_changeが呼ばれている

これが怪しい?

しかしこいつは犯人ではなかった

もう一度call graphをみる

blkdev_getのあたりから分岐してそう / 20 14

__invalidate_deviceが網にかかった!

mdadm-900 [001] .... 75491.384221: __invalidate_device <-invalidate_partition

mdadm-900 [001] .... 75491.384235: <stack trace>

=> ftrace_call

=> invalidate_partition

=> rescan_partitions

=> __blkdev_get

=> blkdev_get

=> blkdev_open

Page 15: Kernel fcache-bug

blk_devの中身

このあたり

bd_invalidatedフラグが怪しい

/ 20 15

// こいつでmd_openが呼ばれる

ret = disk->fops->open(bdev, mode);

..

if (bdev->bd_invalidated) {

if (!ret)

rescan_partitions(disk, bdev);

else if (ret == -ENOMEDIUM)

// この中で__invalidate_paritionが呼ばれる

invalidate_partitions(disk, bdev);

Page 16: Kernel fcache-bug

位置関係

/ 20 16

hard disk

md driver

monitor daemon

invalidate_partition disk driver

md daemon

use space

kernel blk_dev

clear page cache

md_open bd_invalidated

Page 17: Kernel fcache-bug

bd_invalidatedフラグを探す

クリアしているのは初期化と二カ所

どちらもcheck_disk_size_change()を呼んで からbd_invalidatedを0クリア

/ 20 17

rescan_partitions() {

....

disk->fops->revalidate_disk(disk);

check_disk_size_change(disk, bdev);

bdev->bd_invalidated = 0;

invalidate_partitions() {

...

set_capacity(disk, 0);

check_disk_size_change(disk, bdev);

bdev->bd_invalidated = 0;

Page 18: Kernel fcache-bug

クリアし忘れ?

しかし,revalidate_disk()の中ではcheck_disk_size_change()を呼んだあとクリアしていない

このせいでopenするたびに毎回キャッシュクリア

結局LVMでもmdadmでもなくkernel/fsが原因

/ 20 18

revalidate_disk() {

...

mutex_lock(&bdev->bd_mutex);

check_disk_size_change(disk, bdev);

// bdev->bd_invalidated = 0; がない

mutex_unlock(&bdev->bd_mutex);

Page 19: Kernel fcache-bug

動作確認

クリアするようにしてkernelをビルド

キャッシュクリア問題は起こらない

てか,そもそもcheck_disk_size_change()の中でクリアすればええんとちゃうか?

/ 20 19

Page 20: Kernel fcache-bug

その後

LinuxのMLに投げる

作法がいろいろある

patchの作り方

scripts/checkpatch.plでパッチフォーマットの検証

patchのMLへの投げかた

git send-emailを使う

題名のつけかたと投げる場所が不適切だったかも

MLに投げたが黙殺

再度別のMLに挑戦

山本さんによる英語メールの推敲(という名の書き直し)

結局パッチを手動で張り付けたものがaccept...

http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=commit;h=9b4b0d9f9f9ad2

/ 20 20