テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(writeboost report) リードキャッシングの説明

リードキャッシングを実装した.

以下は1Mシーケンシャルリードを断続的に流している時のsar. 252-16がキャッシュデバイス, 252-32がバッキングデバイス. バッキングデバイスから16MB/secリード(実はHDD単体でやると70MB/secくらい. 何でこんなにとろいのかは謎. KVM上だとスプリットが悪く出るというのは事実. どういう仕組みかは知らんがとにかく悪くなる. 物理ではここまで悪化しない. 物理サーバでは1割くらいのロス. KVM上でライトブーストを使うのはユースケースとしては除外したいが, HDDはランダムに比べてシーケンシャルが速すぎるので, 腐っても鯛ということでこれでも十分かも知れない. 時間のかかってるランダムが速くなるならばシーケンシャルなんかどうだっていいという言い方. ただし, ユースケースに依る. ファイルサーバとかでは良いが動画入れたりするのはロスが響く)して, 同量をキャッシュデバイスにステージングしている. キャッシュデバイスはサチっているので, ライトバックしようとしているが(ダーティを前提にキャッシュデバイスからシーケンシャルリードする仕組み), 実際にライトバックはしていない. なぜならば全部クリーンだから.

18:38:10          DEV       tps  rd_sec/s  wr_sec/s  avgrq-sz  avgqu-sz     await     svctm     %util
18:38:11     dev252-0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
18:38:11    dev252-16    192.00  65024.00  32768.00    509.33      0.52      2.71      2.03     39.00
18:38:11    dev252-32   4185.00  33464.00      0.00      8.00     24.19      5.78      0.19     80.00
18:38:11     dev251-0   4184.00  33456.00      0.00      8.00     24.27      5.80      0.19     81.00
18:38:11     dev251-1      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
18:38:11     dev251-2      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
18:38:11     dev251-3    192.00  65024.00  32768.00    509.33      0.52      2.71      2.03     39.00
18:38:11     dev251-4   4183.00  33464.00      0.00      8.00     24.72      5.91      0.23     98.00

リードキャッシュの仕組みは, 以下,

  1. バッキングからリードした4KBデータをためる. 今は8M(4K*2000)ためる. 各々をcellと呼ぶ.
  2. フルになったら(ack_countで判断. カウントダウンなので0になったら)ワーカーを起動.
  3. そのワーカーが, そのデータを4Kのクリーンなライトに機能限定したライトパスにつっこむ.

ライトパスを共有しているため, ライトとリードのミックスでもバグが出にくい設計になっている.

あるcellのアドレス領域に対して途中でライトが入った場合は, ステージングはしない. これはcellをキャンセルすることで実現する.

データ構造を見れば分かる. cancelledがint型なのはアトミシティのためです. ここでboolは危険.

struct read_cache_cell {
        sector_t sector;
        void *data;
        int cancelled; /* Don't include this */
        struct hlist_node list;
};

struct read_cache_cells {
        u32 size;
        u32 threshold;
        struct read_cache_cell *array;
        struct hlist_head *heads;
        u32 cursor;
        atomic_t ack_count;
        struct workqueue_struct *wq;
};

現状では, すべての4KBリードデータはキャッシュされるがスレッショルドを設定して, 128KBより小さいデータはキャッシュしないということも実現する予定(cellをキャンセルすることで実現する). だが, データは4KBにスプリットされているため, この分離された処理から連続を見つけるのは色々と選択肢がある.

  1. 4KBリードする領域を追い続けること. キャッシュ予定のcellのうち, 該当するものをキャンセルすればよい. だがこの実装は, 例えば2スレッドから異なるシーケンシャルリードを流し込んだ時にうまくいかない. デバイスのコードから見ると, 例えばデータが交互に来てしまうからだ.
  2. cellをライトする時にソートして連続領域を見つける. これも完璧ではない.
  3. 4KB前のデータを探す. 連続領域をリンクしていき, 末尾のカウントを上げていき, しきい値以上ならばリストを全部キャンセルする.
  4. dmのコードに手を入れて, スプリットされたbioから元bioを引けるようにする.

将来的に4にすることを見込んで, 今は1あたりで軽く作っておくのが良いだろうと今のところ思っている. 何かいい案があったらください.