DM-cache

先日、ここに書いたFlashcacheはdm-cacheというキャッシュ機構をベースにしているのだが、こちらの方が実装がより簡素だったのでこちらを読むことにする。
dm-cache
このソフトは名前の通りdevice mapperの仕組みを使ってブロックデバイスを別のブロックデバイスでキャッシュをしようというもの。主たる目的としてはSAN、iSCSIAoEなんかのアクセスコスト(あるいはレイテンシが大きい)の高いストレージをDASでキャッシュして高速化しようというもののようだ。
実装はヘッダファイルすら分れていないCのコードで、コメントも含めて2000行未満という非常にコンパクト。これならば読めるかなと

初期化

dm_cache_init

  • バックグランド用の初期化処理
  • 待ち行列を一つ生成
  • do_workをwork queueのカーネルスレッドとして初期化
  • device mapperに操作関数の構造体を登録

操作構造体

いわゆるファイルシステムにおけるVFSみたいなアレ

  • コンストラクタ cache_str
  • デストラクタ cache_dtr
  • マッパー cache_map
  • 状態取得 cache_status

コンストラク

モジュールロード時じゃなく、実際にdevice mapperでセットアップされたときに実行

  • 引数チェック
  • cache_c(後述)確保
  • キャッシュ対象とキャッシュ自体のデバイス確保
  • cache_c初期化
    • 非同期I/O用の領域確保
  • 不揮発キャッシュならメタデータを読み込み(load_metadata<->dump_metadata)
  • 各種統計情報の初期化
  • キャッシュ構造の組み上げ

デストラク

マッパー

device mapper周りはほとんど初見なので大きく外しているかもしれないが、ブロックデバイスに対する操作は一元的にここが呼ばれるみたい。

  • ブロックIO操作からキャッシュの対象ブロックを算出
  • キャッシュのlookup
    • キャッシュヒット
    • キャッシュミス
    • ライトバック
  • それ以外はアクセス先のデバイスフォワード

device mapperはマッパーというだけあって、あるデバイスへの操作を別の操作に写像する機能を作るための仕組み。
linux-2.6/drivers/md以下に実装があって、dm-linear.cが凄くシンプルだった。マッパー内で処理が完結したか、向け直した先で継続するかは戻り値で判断しているみたい(読んでない)で、以下のような宣言があった。

/*
 * Definitions of return values from target map function.
 */
#define DM_MAPIO_SUBMITTED      0
#define DM_MAPIO_REMAPPED       1
#define DM_MAPIO_REQUEUE        DM_ENDIO_REQUEUE

ただ、dm-cacheではこの宣言は使っておらず定数がハードコードされていた。

lookup処理

  • キャッシュはセットアソシアティブ方式で管理されているので、連想数だけ線形探索
    • 対象ブロックを保持しているキャッシュを見付けたら抜ける
    • LRUで置き換え候補用に古いエントリも調べつつ
  • ヒットしていたら1
  • ミス(かwriteback)だったら置き換えるブロックを返す

ヒット時

  • 読み込み
    • キャッシュが有効なら向け直して終わり
    • それ以外なら自力でBIOを発行し、I/O処理を完結
  • 書き込み
    • ライトスルーならデータを無効化、向け直して、リマップとして抜ける
    • 以降はライトバック時
    • キャッシュ状態をダーティに
    • ライトバック中、データがまだ無いなら自力でBIOをリストに繋いでI/O処理を完結
    • それ以外ならキャッシュデバイスに向けて、マップ処理を抜ける

読み込みミス時

cache_read_miss()

  • 統計情報の更新
  • メタデータの更新
  • 遅延処理用の構造組み立て
  • エンキュー

書き込みミス時

cache_write_miss()

  • ライトスルーなら向け直してサヨウナラ
  • 場所計算
  • 統計情報更新
  • メタデータから更新
  • ダーティフラグ立て
  • 遅延処理用の構造組み立て
  • エンキュー

ライトバック

write_back()
キャッシュから対象ブロックデバイスに書き戻して、キャッシュをクリーンにする
もしかして、キャッシュが一杯になるまで処理が実行されない?

遅延処理

do_work()
リストに繋がれた3つの処理を実行するkernel thread。完了したI/Oの後の辻褄合わせ、ページ取得が必要なI/O、実際のI/Oの3つ。
いずれもI/Oの先はasync処理になっている

スーパーブロック的な構造体

cache contextを保持するstruct cache_cがそれか

キャッシュエントリ毎の情報

inode的な情報cacheblock構造体

Flashcacheとの違い

  • エラー処理が適当
  • ダーティページの書き戻し処理
  • 非同期I/O周りが簡素
  • I/Oをまとめて処理する機構がない

雑感とか

  • キャッシュのセット内はわりと素朴な線形探索
  • キャッシュ容量に対してメモリ上に保持する情報が結構大きくないかな?こんなもん?
  • ライトバックが必要な状態で書き戻しが間に合わないうちに更にリクエストが来たら詰まるような
  • ライトスルー時にキャッシュに乗せる処理はしないのね。I/O完了を待ち合わせとか面倒だからかな
  • dmのブロック算出用のマクロがちょっと混乱しそうになる