テストステ論

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

device-mapperのAPI変更 紹介

昨日, 私の開発しているキャッシュモジュールdm-lcの開発を再開しました. dm-lcは, カーネルバージョン3.2以降をサポートする予定です. そして, 以前盛んに開発していた時は, 3.6を使ってテストをしていました.

複数のバージョンを入念にテストすることは不可能ですし, そもそも意味があると思いません. 本質的に重要なのは, アルゴリズムとコードが出来るだけポータブルであることです. その上で, 各バージョンでコンパイルが通ればOKというのが理想です. また, テストをするにしても, 頭と尻尾だけをやって真ん中は知らんぷりするという戦略でも, 十分と思います. 3.x以降も挙動が変わり続けているサブモジュール(ワークキューとか)もあるのですが, そういうものに対しても, 細かい挙動にべったりと依存して最善を狙うより, 少し離れたところに立ち, 可搬性のあるコードを書いて次善を狙うという戦略によって, 細かい挙動の変化に左右されないようにすることが出来ます. キャッシュモジュールの複雑な挙動の中で, 一部分だけ最適化しても全体としては小さいことが多いということと, 一般的にソフトウェアというのは, 基本的なAPIに比重を置いた最適化をするはずであるという経験則に従っています. Linuxカーネルは, Out-of-tree modulesについては存在しないも同様だという立場をとってますから, 当然の戦略といえます.

複数バージョンでコンパイルは, 設計における羅針盤になります. たくさんのバージョンでコンパイルが通るということは, 設計としてポータブルであり, 今後もコンパイルが通り続ける可能性が高いことを意味するからです. dm-lcは, 以下のようなスクリプトで複数バージョンのコンパイル自動化を行なっています. 自動化は, ソフトウェアの本質です.

rm portable/result/*

SUMMARY=portable/result/summary
echo -n > ${SUMMARY}

for kern_version in 3.2.36 3.4.25 3.5.7 3.6.9 3.7.2 3.8.5 3.9.8
do
  echo ${kern_version}
  export KERN_VERSION=${kern_version}
  make clean
  make 2> portable/result/${kern_version}

  echo --${kern_version}-- >> ${SUMMARY}
  grep "error" portable/result/${kern_version} >> ${SUMMARY}
  python filter_log.py portable/result/${kern_version} >> ${SUMMARY}
done

私がdm-lcの開発を中止してから4ヶ月が経ちました. その間に母体カーネルは3.9をリリースし, 3.10の開発に進んでいます. dm-lcも立ち止まってはいられません. しかし, 4ヶ月も開発をサボっていた浦島太郎には, 試練が待っていました. それが, device-mapperの(大幅な)API変更です. device-mapperでは3.6以降, 根幹となるAPIの設計変更が取り込まれています. それを今日は説明します.

--- 以下本題 ---

3.6

statusメソッドにunsignedなフラグパラメータが追加されました. 以下は, dm-lcの対応例です.

static int lc_mgr_status(
                struct dm_target *ti, status_type_t type,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
                unsigned flags,
#endif
                char *result, unsigned int maxlen)

他にも, mempoolのAPIが変更されたりなどがあったかと思いますが, 記憶がありません.

3.7

大きな変更はありません.

3.8

per-bio structure

mapとendioメソッドから, map_context引数が削除されました. map_context(::map_info)引数は, endioまで持っていくことが出来るため, mapとendioの間でコミュニケーションをする場合に使うことが出来ました. Don't use map_info and use a per-bio structure

これを使うためには, dm_targetのper_bio_data_sizeを設定する必要があるのと, per-bioデータの取得をdm_per_bio_dataで行う必要があります. 3.9の時点で, map_infoに依存したコードが完全にクリーンされているわけではありませんが, そのうちクリーンされるのだと思います. 以下のような意味でしょうか?

  • per-bioのデータであればbioに直接結び付けられるべきである.
  • どのターゲットでもmap/endioのコミュニケーションが必要ではないから, 必要ないものに関してはアロケートしないようにする.
  • もともと, bio->bi_privateを使っていたが, マナーとして良くないし, cloneの部分領域を余分に確保して使う方がアクセス性能も高い.

ある程度の理解は出来ますが, map/endioというdevice-mapperの本質に思い切り手を入れることはねーだろうよという気はします. Out-of-tree moduleは辛い.

statusの返り値がvoidに

どういう理由かはまじめに読む気が起きませんが, statusを表示するだけなのに0(成功)以外を返す方がおかしいのでint返しはやめてvoidにしようということでしょう. たぶん. https://patchwork.kernel.org/patch/2194841/

3.9

num_flush_requests, num_discard_requestsがrename

これは単なるリネームですから, 素直に従って死ぬことはありません. 背景には, request-based device-mapperと名前を明確に分けたいという気持ちがあるのだと思いますが, あまりにも潔癖すぎると思います. Out-of-treeへのイジメだ.
http://gitorious.ps3dev.net/ps3linux/dm-bswap16/commit/8fd9e006e9de2d67842665a702cb20f5b3b11672?format=patch

+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
    ti->num_flush_requests = 1;
    ti->num_discard_requests = 1;
+#else
+   ti->num_flush_bios = 1;
+   ti->num_discard_bios = 1;
+#endif

hlist_for_each_entryからhlist_nodeを辿るパラメータが消滅

hlistは, ハッシュテーブルを実装する時に便利なクラスです. dm-lcの中でも使っています. entryを辿るって言ってるのに, なんでnodeも辿ってるんだよというのは前々からかなり突っ込みどころでしたが, ようやく修正されました. 3.2より前に修正すべきだった. http://lwn.net/Articles/541446/

以上です. per-bioデータへの対応をシンプルに行う方法が見つからず苦戦しています.