カーネル/VM Advent Calendar 6日目: make *config 〜カーネルいじっちゃお!〜

カーネル/VM Advent Calendar

この記事は http://atnd.org/events/10701 のために書かれました。

Linux カーネルを設定しよう

Linux カーネルは多種多様なアーキテクチャに対応して、数多くの設定項目があります。 distroカーネルをそのまま使うのもいいですが、自分の環境・自分の好みにあわせてカーネルを設定し、コンパイルするのもまた楽しいものですよ!よ!

カーネルソースのディレクトリに cd して "make <設定方法>" することで、カーネルの設定を行なうことができます。設定方法には以下のものがあります。

  • config
  • nconfig
  • menuconfig
  • xconfig
  • gconfig
  • oldconfig, silentoldconfig, oldnoconfig
  • localmodconfig, localyesconfig
  • allnoconfig, allyesconfig, allmodconfig, alldefconfig
  • randconfig

以下、それぞれ簡単に説明します。

config

全ての項目をひとつひとつ懇切丁寧に聞いてきます。 twitter がない時のひまつぶしや、寝れない夜などに利用します。

"y" でカーネルに組み込み、 "n" で機能無効、 "m" でカーネルモジュールになります。なんじゃこりゃ?と思った時は "?" で説明を見ることができます。

nconfig

最近あらわれた新しい設定方法。 curses base です。

F1 操作のヘルプ表示
F2 項目のヘルプ表示
F3 簡単な操作説明
F4 全ての項目表示 <-> (無効などで)隠されている項目非表示の切り替え
F5 一つ上の階層へ
F6 保存
F7 ロード
(F8) (シンボルの検索)
(/) (ページ内検索)

() の中に入っているものは 2.6.37 から使えるようになる予定です。ページ内検索は menuconfig にない便利で気がきいた機能ですし、隠れているシンボルを一時的に表示させることで他の設定の影響で無効になっているものも簡単に見つけだしなぜそれが隠れているのかを調べることができます。

環境変数 NCONFIG_MODE=single_menu としておくと、階層メニューではなくなり、一つのページでツリーを開いたり閉じたりするような動作になります。

menuconfig

コンソールで昔から使われてきた設定方法だと思います。 "/" で設定項目全体からの検索ができます。

xconfig

qt4 base でグラフィカルな設定エディタになっています。こんな感じ。

検索してその画面のまま、設定の変更ができる様子、なかなかいい。


gconfig

こちらは gtk base です。まぁだいたい同じ見ため。


しかし、こちらは検索ができないようです! gtk が好きで kernel いじってみたい人はぜひここに patch を!(え

oldconfig, silentoldconfig, oldnoconfig

oldconfig 系列。 oldconfig をいまある .config (設定ファイル) をもとに追加された設定項目を聞いてきてくれます。 silentoldconfig は聞いてこずにデフォルトにして、そして oldnoconfig は追加項目の設定を No にします。

localmodconfig, localyesconfig

localmodconfig は現在の設定から「カーネルに読みこまれていない module」を無効にした設定を行ないます。読みこまれていれば、そのままなので「ある程度」カーネルの設定をマシンに最適化するのには使えますが、「ある程度」以上には使えないと思います。 (kvm なんかの module はデフォルトでは読みこまれないから、これで無効になってしまうんじゃあないだろうか…)

localyesconfig はさきほどのものを module ではなくカーネル組み込みにするだけの違いです。

allnoconfig, allyesconfig, allmodconfig, alldefconfig, randconfig

順番に

  • 全てを無効 (最小限になります)
  • 全てをカーネルに組み込み
  • 全てを module にする
  • 全てをデフォルトにする
  • 全てをランダムに設定

ここらはビルドテスト用に使われますね。特に allyesconfig, randconfig なんかでビルドできないような変更をしてないかどうか確認するのによく使われています。

裏側

*config で使われる設定ツールのソースは linux-2.6/scripts/kconfig/ に入っています。

ほとんどはやっぱり C で書かれていますが、 localmodconfig, localyesconfig については streamline_config.pl という Perl スクリプトで書かれています。これは、 ソースツリー・ config ファイル・lsmod の結果を書いたファイルを引数にとって、 読まれていないものをオフにしたものを出力します。 localyesconfig は、その結果を「単純に sedで s/=m/=y/」 しているだけなのでここには注意が必要です。自分で設定した・自動で設定したにかかわらず全ての module が組み込みになってしまうわけです。

gconfig に検索機能をいれたい方はここの gconf.c をいじってください!

こぼればなし

この config いじるやつらはどうにも権限チェックがあいまいで困ります。

たとえば make config の .config への書きこみは最後に行なわれていて、うっかり user で make config で根気よくひとつひとつ設定して終わった!!! -> 書きこみ -> そこで パーミッションチェックが行なわれ、 .config が root のものだったりすると…… -> 書きこめませんでした! -> そのまま他の保存場所なんか聞かずにエラー終了

問: …あれ…ぼくのがんばって設定したものは…どこ…に? 答: /dev/null に保存しておいたよ!おにいちゃん!

という悲しいことになります。このゆゆしき問題については @hiromu1996 せんせーにすでに嘆いているので、きっと彼が解決してくれるでしょう!!! パッチ期待してます! もうがんばって設定した結果を無駄にしたくないのです!!!

Kernel/VM Advent Calendar 2日目: PV ticketlock ってなんですか><

今日もゆっくり Xen の ML をながめているとこんなスレッドを発見

http://permalink.gmane.org/gmane.comp.emulators.xen.devel/93598

ticket lock? "without expandig spinlock"? lock の一種か? なんじゃこりゃ?

ということで調べてみました。

そもそも spinlock って

spinlock は Linux でよく使われている lock のひとつ。基本的に

  • lock とる
    • とれなかったらとれるまで spin する (無限ループ)
  • なんかやる
  • lock 解除

というもので、 1. lock の中身が短く かつ 2. lock の中で sleep しない 場合に使われます。

その実装はアーキテクチャによりますが、 x86 では昔はこのようになっていました。

  • lock に使われる数値がある
  • 最初は その「lock値」は "1"
  • lock 取得時に atomic に「lock値」を 1 減らして、結果が 0 かどうかを見る
    • 結果が負であれば spin する
  • なんかやる
  • 「lock値」を "1" に戻す

そして ticket lock へと

この実装はとても速く、また「lock値」を見てやれば "どれぐらいのスレッドが同時に lock をとろうとしているか" がわかるというメリットがありました。

しかし、ひとつのスレッドの処理が終わった後、次にどのスレッドが起動されるかわからない という「アンフェアな」デメリットがあり、これはレイテンシにも悪影響になります。

lock値 スレッドA スレッドB スレッドC スレッドD スレッドE
1 - - - - -
0 lock - - - -
-1 working lock - - -
-2 working spin lock - -
-3 working spin spin lock -
-4 working spin spin spin lock
1 unlock spin spin spin spin
0 - spin spin spin lock

このように、ずっと待ってたスレッドをさしおいて スレッドEが走ることもありうるわけです。

ということで、これを解消するべく ticket spinlock が導入されました。

ticket spinlock は "Next" と "Owner" という2つの数値を使い、銀行やら役所にある整理券と同じような動きをします。 "Next" が「入口に置いてある整理券」 "Owner" が「窓口で現在処理中の整理券番号の表示」となります。

  • 初めは "Next" = "Owner" = 0 で開始
  • "Next" を atomic に 1 増やす (整理券をとる)
  • 増やす*前*の "Next" == "Owner" かどうか確認 (取った整理券が窓口表示の番号と同じなら窓口へ)
    • 違ったら spin しながら 「増やす*前*の "Next" == "Owner"」になるのを待つ (窓口表示を見ながら待ちます)
  • なんかする (窓口でいろいろ)
  • "Owner" を atomic に 1 増やす (窓口表示が 1 増える)

こうすることで、さっきのケースでも A->B->C->D->E という順番が保証されるようになります。

Next Owner スレッドA スレッドB スレッドC スレッドD スレッドE
0 0 - - - - -
1 0 lock - - - -
2 0 working lock - - -
3 0 working spin lock - -
4 0 working spin spin lock -
5 0 working spin spin spin lock
5 1 unlock spin spin spin spin
5 1 - lock spin spin spin
5 1 - working spin spin spin

PV spinlock

この spinlock ですが、 VM の中で動いているゲストの spinlock 取得は効率があまりよくなく、特に ticket spinlock とはかなり相性が悪いようです。 (Xen のスケジューラのパフォーマンスに依存してしまう)

http://www.valinux.co.jp/contents/tech/eventreport/0806xensummit.html

特に最近のLinux kernelに取り込まれた「ticket spin lock」と仮想環境とは相性が悪くカーネルコンパイルでCPUサイクルの99%以上がスピンロック取得に無駄に使われ、10sの仕事に45分無駄にされていることになる。いくつかの提案と改善されたベンチマーク結果が示された。

動画: http://vimeo.com/12738341
スライド: http://www.xen.org/files/xensummitboston08/LHP.pdf

と、そこで今では "PV spinlock" という guest 時専用の spinlock 処理で、 guest 上の spinlock を置き換えています。

これは基本的には昔の spinlock と変わりません。違いは

  • 2^16 回だけ spin する
  • それでも lock がとれなければ、 cpu ごとの event channel に登録して block する (ようするに mutex ぽく sleep するみたいなものでしょう…)

この 「2^16 回」というのは 「98%のlockが取得できる回数」です。 (参考 Thomas Friebel: Xen Summit talk "Preventing Guests from Spinning Around") これによって残り2%の時間を食う lock を寝させて処理の時間を削減できるようになります。

やっと PV ticket lock

さて、ここまでで guest 上の spinlock の問題は解決したわけですが、この実装は「基本的には昔の spinlock と変わらない」のでもちろん昔の spinlock にあった問題が残っていますし、さらに二つの別の問題もあります。

  • 物理マシンでの spinlock と実装が異なる
  • spinlock 関係の全ての処理に "PV spinlok" をするための処理の overhead が必要

これを変更し

  • 上限回数までは ticket spinlock する
  • それでも lock できなかった場合に、 Xen に処理を渡し block する

というのが、 "PV ticket lock" になります。 これによって overhead は

  • lock をとろうと指定回数だけ spin する処理の部分
  • block していた時に block していたものを起こす処理の部分

だけになります。後者はあまり起こらないケースなので、ほとんどの場合 lock をとろうとする時だけ overhead がかかるようになるわけです。




ということで、結構良さげなパッチなのですが……

This code survives for a while with moderate testing, (make -j 100 on
8 VCPUs on a 4 PCPU system), but locks up after about 20 iterations,
so there's still some race/deadlock in there (probably something
misordered), but I think the basic approach is sound.

まだどっかで deadlock してるんかいっ…!


まぁ…今後修正されるでしょう…。 kvm にもメリットありますし期待ですね。

Kernel/VM Advent Calendar 1日目: kmemcheck にご用心!

Kernel/VM Advent Calendarに本来考えてたネタをやろうとVMの中のカーネルをいじっていたところ、ふと気がつく。

Tuxくんが一人しかいない…? VMでは4コアあげているしなぜ?

確かに /proc/cpuinfo を見ても CPU が1つしか使われてないのだ。

その時とった dmesg -> https://gist.github.com/731682

[ 0.000000] SMP: Allowing 4 CPUs, 0 hotplug CPUs

CPU4つあるよね……

[    0.140772] Brought up 1 CPUs
[    0.140784] Total of 1 processors activated (4787.87 BogoMIPS).

1つしか起動されない…

[    0.024100] CPU0: Intel QEMU Virtual CPU version 0.13.0 stepping 03
[    0.140155] kmemcheck: Limiting number of CPUs to 1.
[    0.140171] kmemcheck: Initialized

……ん? これか! "kmemcheck: Limiting number of CPUs to 1."

ということで、 kmemcheck のお話です。

もともと、 kmemcheck は Linux カーネルデバッグ用の機能。 カーネルのコードの中で初期化されずに使われるメモリがないかどうかをチェックする valgrind のようなもの。 これを有効にしていると、デスクトップ用途には使いづらくなる…らしいです。 (linux/Documentation/kmemcheck.txt)

んで、この kmemcheck の初期化が

int __init kmemcheck_init(void)
{
#ifdef CONFIG_SMP
	/*
	 * Limit SMP to use a single CPU. We rely on the fact that this code
	 * runs before SMP is set up.
	 */
	if (setup_max_cpus > 1) {
		printk(KERN_INFO
			"kmemcheck: Limiting number of CPUs to 1.\n");
		setup_max_cpus = 1;
	}
#endif

こんな風に SMP が使われていたら 1つのCPUしか使わせないという… それ CONFIG_SMP を Disable にしてくれよ… orz なコードになっていまして、こいつのせいで1つのCPUしか見えてこない、というわけでした。 (さらに言えば、デフォルトオフにしてても CPU 1つにされてしまう…まぁしかたないけど)

対策: kmemcheck を使わない しか現状なさそうです。まぁ確かに遅いしいいけど…

SMP 対応パッチも http://www.kerneltrap.org/mailarchive/linux-kernel/2008/5/23/1920164 にあるのですが、議論がいつのまにか忘れられてしまったのか中途半端な状態に…。 このスレッドだと(多分 qemu のバグで) qemu じゃ動かない、と言っていますが今はどうなんでしょうね。 やってみて Advent Calendar にしようかと思います。