Slide 1

Slide 1 text

device mapperによる ディスクI/O障害のエミュレーション ~ カーネルモジュール自作編 ~ Dec, 11st, 2023 Satoru Takeuchi X: satoru_takeuchi 1

Slide 2

Slide 2 text

話すこと ● 「その63 device mapperによるディスクI/O障害のエミュレーション ~既存ターゲット 編」の続き ● ディスクI/O障害をエミュレーションするdevice mapperターゲットをカーネルモ ジュールとして自作 ● 簡単な2つのターゲットを作る ○ https://github.com/satoru-takeuchi/0065-dm-io-failures-develop 2

Slide 3

Slide 3 text

dm-simple ● dm-linearのコードから必要最小限の部分以外を削って 100行強にしたもの ● マップ先にあるデバイスにI/Oをそのまま受け渡すだけ ● テーブルの形式 3 <開始セクタ> <終了セクタ> simple <マップ先デバイス> write dm-simple disk write read read

Slide 4

Slide 4 text

ターゲットをあらわす構造体を作る 4 static struct target_type simple_target = { .name = "simple", .version = {0, 0, 1}, .features = DM_TARGET_NOWAIT | DM_TARGET_PASSES_CRYPTO, .module = THIS_MODULE, .ctr = simple_ctr, .dtr = simple_dtr, .map = simple_map, .iterate_devices = simple_iterate_devices, }; dmデバイスのI/Oを下のデバイスに渡す

Slide 5

Slide 5 text

マップ処理 5 static int simple_map(struct dm_target *ti, struct bio *bio) { struct simple_c *sc = ti->private; bio_set_dev(bio, sc->dev->bdev); return DM_MAPIO_REMAPPED; }

Slide 6

Slide 6 text

マップ処理 6 static int simple_map(struct dm_target *ti, struct bio *bio) { struct simple_c *sc = ti->private; bio_set_dev(bio, sc->dev->bdev); return DM_MAPIO_REMAPPED; } 個々のI/Oを管理するデータ ● I/Oに使うメモリ領域 ● I/O対象デバイス

Slide 7

Slide 7 text

マップ処理 7 static int simple_map(struct dm_target *ti, struct bio *bio) { struct simple_c *sc = ti->private; bio_set_dev(bio, sc->dev->bdev); return DM_MAPIO_REMAPPED; } I/O対象デバイスを マップ先デバイスに書き換え

Slide 8

Slide 8 text

マップ処理 8 static int simple_map(struct dm_target *ti, struct bio *bio) { struct simple_c *sc = ti->private; bio_set_dev(bio, sc->dev->bdev); return DM_MAPIO_REMAPPED; } I/Oをマップ先デバイスに リマップしたことを呼び出し元に伝える

Slide 9

Slide 9 text

使用例 9 $ cat test-simple.txt 0 2097152 simple /dev/loop0 $ sudo dmsetup create test-simple test-simple.txt dmデバイスの作成

Slide 10

Slide 10 text

使用例 10 $ cat test-simple.txt 0 2097152 simple /dev/loop0 $ sudo dmsetup create test-simple test-simple.txt dmデバイスの作成 $ sudo dd if=test-simple.txt of=/dev/mapper/test-simple dmデバイスにwrite

Slide 11

Slide 11 text

使用例 11 $ cat test-simple.txt 0 2097152 simple /dev/loop0 $ sudo dmsetup create test-simple test-simple.txt dmデバイスの作成 dmデバイスにwrite $ sudo dd if=test-simple.txt of=/dev/mapper/test-simple … $ sudo dmsetup remove test-simple $ sudo hexdump -c -n 128 /dev/loop0 0000000 0 2 0 9 7 1 5 2 s i m p l e 0000010 / d e v / l o o p 0 0 \n \0 \0 …

Slide 12

Slide 12 text

dm-hello ● writeしてくれた人にあいさつをする ● テーブルの形式 12 <開始セクタ> <長さ> hello <マップ対象デバイス>

Slide 13

Slide 13 text

マップ処理 13 static int hello_map(struct dm_target *ti, struct bio *bio) {   ... bio_for_each_segment(bvec, bio, iter) { char *segment; struct page *page = bio_iter_page(bio, iter); if (unlikely(page == ZERO_PAGE(0))) break; segment = bvec_kmap_local(&bvec); memcpy(segment, hello, sizeof(hello)); kunmap_local(segment); break; } bio_set_dev(bio, hc->dev->bdev); return DM_MAPIO_REMAPPED; }

Slide 14

Slide 14 text

マップ処理 14 static int hello_map(struct dm_target *ti, struct bio *bio) {   ... bio_for_each_segment(bvec, bio, iter) { char *segment; struct page *page = bio_iter_page(bio, iter); if (unlikely(page == ZERO_PAGE(0))) break; segment = bvec_kmap_local(&bvec); memcpy(segment, hello, sizeof(hello)); kunmap_local(segment); break; } bio_set_dev(bio, hc->dev->bdev); return DM_MAPIO_REMAPPED; } bio内の全領域(データの断片)を走査

Slide 15

Slide 15 text

マップ処理 15 static int hello_map(struct dm_target *ti, struct bio *bio) {   ... bio_for_each_segment(bvec, bio, iter) { char *segment; struct page *page = bio_iter_page(bio, iter); if (unlikely(page == ZERO_PAGE(0))) break; segment = bvec_kmap_local(&bvec); memcpy(segment, hello, sizeof(hello)); kunmap_local(segment); break; } bio_set_dev(bio, hc->dev->bdev); return DM_MAPIO_REMAPPED; } 先頭領域に”Hello!”という文字列を書き込む

Slide 16

Slide 16 text

マップ処理 16 static int hello_map(struct dm_target *ti, struct bio *bio) {   ... bio_for_each_segment(bvec, bio, iter) { char *segment; struct page *page = bio_iter_page(bio, iter); if (unlikely(page == ZERO_PAGE(0))) break; segment = bvec_kmap_local(&bvec); memcpy(segment, hello, sizeof(hello)); kunmap_local(segment); break; } bio_set_dev(bio, hc->dev->bdev); return DM_MAPIO_REMAPPED; } 書き終わったら走査終了 先頭領域以外は触らない

Slide 17

Slide 17 text

使用例 17 $ cat test-hello.txt 0 2097152 hello /dev/loop0 $ sudo dmsetup create test-hello test-hello.txt dmデバイスの作成

Slide 18

Slide 18 text

使用例 18 $ cat test-hello.txt 0 2097152 hello /dev/loop0 $ sudo dmsetup create test-hello test-hello.txt $ sudo dd if=test-hello.txt of=/dev/mapper/test-hello … $ sudo hexdump -c -n 128 /dev/mapper/test-hello dmデバイスの作成 dmデバイスにwrite

Slide 19

Slide 19 text

使用例 19 $ cat test-hello.txt 0 2097152 hello /dev/loop0 $ sudo dmsetup create test-hello test-hello.txt $ sudo dd if=test-hello.txt of=/dev/mapper/test-hello … $ sudo hexdump -c -n 128 /dev/mapper/test-hello 0000000 H e l l o ! \n \0 2 h e l l o 0000010 / d e v / l o o p 0 0 \n \0 \0 \0 … dmデバイスの作成 dmデバイスにwrite データが書き換わった

Slide 20

Slide 20 text

参考になるコード ● ターゲット作成時にパラメタを渡して挙動を変更 ○ https://github.com/torvalds/linux/blob/v5.15/drivers/md/dm-flakey.c#L184-L261 ● ターゲット作成後にmessageを送って挙動を変更 ○ https://github.com/torvalds/linux/blob/master/drivers/md/dm-dust.c#L414-L511 ● 障害を一定確率で発生させる ○ https://github.com/torvalds/linux/blob/master/drivers/md/dm-flakey.c#L529-L532 20

Slide 21

Slide 21 text

まとめ ● device mapperターゲットの自作により、好きなようにディスクI/O障害をエミュレー ションできる ● カーネルモジュールの作り方 ○ https://github.com/torvalds/linux/blob/master/Documentation/kbuild/modules.rst ● device mapperのコード ○ https://github.com/torvalds/linux/blob/master/drivers/md/ ● 📝 Rustでdevice mapperのターゲットを書けるようにする試みも ○ https://lore.kernel.org/lkml/ZJm%2FJfIPMrgXkYpp@boqun-archlinux/T/ 21