[kernel] カーネル/VM Advent Calendar 2011 2日目 #kernelvm

この記事はhttp://atnd.org/events/21910のために書かれたものです。

今回の記事では TranscendentMemory, frontswap, zcacheを紹介します。

TranscendentMemory とは

簡単に言えばLinuxカーネルの新しいメモリ管理システムです。TranscendentMemoryは

  • サイズが不定で知ることができない
  • 特殊なput/getの関数でページ単位で「ハンドラ」を使って読み書きする
  • 設定によっては書いたものが消えてしまうこともある

抽象的なメモリになっています。こんなにいかにも使いにくいのに一体どんなメリットがあるんでしょうか?

このようにメモリを抽象化することで、このシステムの裏側でいろいろと面白いことができるようになります。たとえば

  • メモリデータを圧縮 (zcache) (2.6.39でstagingにマージ)
  • 高速な回線を使ってP2Pでメモリデータをやりとり (RAMster)
    • あるいはメモリサーバに送信
  • SSDやNVRAMを「拡張メモリ」として活用
  • VMのメモリをHypervisorで圧縮・dedup

などの操作が抽象化されていることによってやりやすくなります。

さて、以上のように TranscendentMemoryの裏側の例を紹介してきましたが、逆に表側、 TranscendentMemory にput/getする側はどうなっているのでしょうか。

put/getするfrontend側は

  • ファイルシステムのcleanなファイルキャッシュを扱う cleancache (3.0にマージ)
    • メモリがたりなくなれば TranscendentMemory から捨てられる
  • swapをフックして TranscendentMemory に送る frontswap

とがあります。これをカーネルでONにしてbackend driverを適切に設定してやれば、あとはカーネルで適当にメモリ圧縮してくれるわけですね。

Frontswap+cleancache+zcacheを使ってみる

さて、せっかくなのでfrontswapなどを使ってみましょう。

当然みなさんgitでLinuxカーネルを取ってきてるでしょうから

$ git remote add tmem git://oss.oracle.com/git/djm/tmem.git
$ git fetch tmem
$ git merge tmem/frontswap-v11

このようにしてpatchをとりこんでください。mm/swapfile.cがconflictしますが

<<<<<<< HEAD
        err = try_to_unuse(type);
        compare_swap_oom_score_adj(OOM_SCORE_ADJ_MAX, oom_score_adj);
=======
        err = try_to_unuse(type, false, 0); /* force all pages to be unused */
        test_set_oom_score_adj(oom_score_adj);
>>>>>>> tmem/frontswap-v11

        err = try_to_unuse(type, false, 0); /* force all pages to be unused */
        test_set_oom_score_adj(oom_score_adj);

こんな感じに直せば大丈夫です。zcache,frontswap,cleancacheをカーネルに組み込むようにして、あとはいつものようにコンパイルしてください。

ですが…再起動する前に! zcacheはカーネルの起動パラメータに"zcache"が入っていないと有効にはなりません。忘れずに追加しておいてください。

…ということで再起動します。

Nov 23 06:36:56 [kernel] [    3.769848] zcache: cleancache enabled using kernel transcendent memory and compression buddies
Nov 23 06:36:56 [kernel] [    3.799276] zcache: frontswap enabled using kernel transcendent memory and xvmalloc
Nov 23 06:36:57 [kernel] [    5.288799] zcache: created ephemeral tmem pool, id=0, client=65535
Nov 23 06:36:57 [kernel] [    5.289425] EXT4-fs (sda5): mounted filesystem with ordered data mode. Opts: (null)
Nov 23 06:36:57 [kernel] [    5.289583] VFS: Mounted root (ext4 filesystem) readonly on device 259:655360.
Nov 23 06:36:57 [kernel] [   22.366606] Btrfs loaded
Nov 23 06:36:57 [kernel] [   22.395155] device fsid 5c08bf87-f564-4b2d-8a63-68e38f525614 devid 1 transid 1092270 /dev/sda3
Nov 23 06:36:57 [kernel] [   22.397151] btrfs: enabling auto defrag
Nov 23 06:36:57 [kernel] [   22.397161] btrfs: use lzo compression
Nov 23 06:36:57 [kernel] [   22.397170] btrfs: disk space caching is enabled
Nov 23 06:36:57 [kernel] [   23.640003] zcache: created ephemeral tmem pool, id=1, client=65535
Nov 23 06:36:57 [kernel] [   27.638328] zcache: created ephemeral tmem pool, id=2, client=65535
Nov 23 06:36:57 [kernel] [   27.638337] EXT4-fs (sda5): re-mounted. Opts: (null)
Nov 23 06:36:57 [kernel] [   27.874058] zcache: created persistent tmem pool, id=3, client=65535
Nov 23 06:36:57 [kernel] [   27.874065] Adding 4953084k swap on /dev/sda4.  Priority:-1 extents:1 across:4953084k FS

うまくいけばこのようにファイルシステムのmountやswaponごとにzcacheが TranscendentMemory のpoolを作っているのがカーネルログに出てくるかと思います。

実際動いているの?

$ ls /sys/kernel/debug/cleancache/
failed_gets  invalidates  puts  succ_gets
$ cat /sys/kernel/debug/cleancache/*
24871401
46104226
491298
14126
$ ls /sys/kernel/debug/frontswap
failed_puts  gets  invalidates  succ_puts
$ cat /sys/kernel/debug/frontswap/*
315690
634050
958144
1155149
$ ls /sys/kernel/mm/zcache/
compress_poor           evicted_unbuddied_pages  flush_found              zbud_cumul_zbytes           zv_curr_dist_counts
curr_obj_count          failed_alloc             flush_total              zbud_cumul_zpages           zv_max_mean_zsize
curr_obj_count_max      failed_eph_puts          mean_compress_poor       zbud_curr_raw_pages         zv_max_zsize
curr_objnode_count      failed_get_free_pages    put_to_flush             zbud_curr_zbytes            zv_page_count_policy_percent
curr_objnode_count_max  failed_pers_puts         zbpg_unused_list_count   zbud_curr_zpages
evicted_buddied_pages   flobj_found              zbud_buddied_count       zbud_unbuddied_list_counts
evicted_raw_pages       flobj_total              zbud_cumul_chunk_counts  zv_cumul_dist_counts
$ cat /sys/kernel/mm/zcache/curr_obj_count
201104
$ cat /sys/kernel/mm/zcache/compress_poor
32430

このようにcleancache, frontswapの動作状況が記録されています。

ところで cleancacheの方には failed_getsが、frontswapの方には failed_putsがありますね。上で書いた通り、cleancacheの方はいつでもbackend側でデータを削除してしまってもいいのでfailed_getsがあるわけですね。一方で、frontswapの方は、swapの中身が途中で消えてしまっては大変ですからfailed_getsはありません。その代わりにfailed_getsがあります。 frontswapはbackendにデータを渡しますが、この時にbackendは受け渡し拒否することができます。具体的ははzcacheの場合、メモリデータをあまり圧縮できない時(/sys/kernel/mm/zcache/zv_page_count_policy_percentの設定値 デフォルトで75%)には、受け渡しを拒否して普通のswap deviceに管理するようにしてもらいます。

おまけ

TranscendentMemoryについてはこんなPDFがあっていろいろ載っています。

http://www.linux-kvm.org/wiki/images/d/d7/TmemNotVirt-Linuxcon2011-Final.pdf