NIST により2013/5/14 に、 RHEL6.1 - 6.4 をはじめとする Linux ディストリビューションに、perf のバグをついて権限昇格される脆弱性があることがアナウンスされました。
Vulnerability Summary for CVE-2013-2094
影響範囲が大きいと思いますので、情報をまとめておきました。
Exploit Code:semtex.c
手元のCentOS 6.4 で試したところ、rootを取ることが出きました。
Does CVE-2013-2094 affect Red Hat Enterprise Linux and Red Hat Enterprise MRG?
解決策として、Systemtap スクリプトを guru モードで動作させ、動的にパッチを当てる手法が掲載されています。
RedHat Bugzilla CVE-2013-2094
書き込み「Petr Matousek 2013-05-14 19:36:43 EDT」において、上記のexploit:semtex.cでroot権限昇格についての詳しい説明があります。ありがたや。
IT media「Linuxに権限昇格の脆弱性、エクスプロイトも出まわる」
原因となったコミット
修正コミット
2013/05/16
2013/05/09
Qemu のトレース新機能 "ftrace backend" 紹介
はじめに
Qemu のトレース新機能 "ftace backend" は Linux 標準のトレース機構 ftrace を使って Qemu と Linux Kernel(KVM) のトレース情報を併せて取得する機能です。Qemu で KVM を使う場合は、ユーザ空間(Qemu)とカーネル空間(Kernel)を頻繁に遷移するため、両空間のトレース情報を併せて取得できると、デバッグや性能解析がよりはかどります。
ftrace backend の実装には ftrace marker が用いられています。ftrace marker は debugfs の marker file への書き込みを ftrace のリングバッファに送る機能です。Qemu ftrace backend は、Qemu のトレース情報出力先を marker file にすることで実現しています。
関連コミット:
trace: Add ftrace tracing backend
2013年5月3日に Qemu Mainline にマージされました。おそらく Qemu 1.5 で使用できるはずです。Author の名前が自分と酷似していますが。。。
使い方
ftrace backend を使うためには、まず configure 時に trace backend として "ftrace" を指定する必要があります。
このままでも ftrace backend はトレース情報を debugfs の trace file に記録できますが、今回は KVM を使うのでKVM 関係のトレースイベント情報も併せて取得するよう設定します。
Qemu 起動時に、取得対象の Qemu trace イベントを指定します。
Qemu のトレースイベント一覧は Qemu ソースコードの trace-events ファイルに記載されています。また、 Qemu monitor から "info trace-events" コマンドによっても取得できます。
ここでは、すべてのイベントを取得するような設定にします。
得られるトレース出力は以下のようになります。
libvirt で使う場合
libvirt で ftrace backend を使う場合は、libvirtd の設定ファイルを以下のように変更し、Qemu を起動するユーザを root にする必要があります。
/etc/libvirt/qemu.conf に以下を追加:
余談
ftrace marker を使うためのライブラリがあれば、もっとftrace backend のコードを短くできますし、他のアプリケーションでも手軽に ftrace marker が使えるようになって便利です。ライブラリ名は、"libftrace" などという名前になるのでしょうか。
最近、ftrace は snapshot 機能や、multiple buffer 機能が追加されています。ユーザ空間のプログラム内からそれらの機能を利用するライブラリがあるとさらに嬉しいです。
Qemu のトレース新機能 "ftace backend" は Linux 標準のトレース機構 ftrace を使って Qemu と Linux Kernel(KVM) のトレース情報を併せて取得する機能です。Qemu で KVM を使う場合は、ユーザ空間(Qemu)とカーネル空間(Kernel)を頻繁に遷移するため、両空間のトレース情報を併せて取得できると、デバッグや性能解析がよりはかどります。
ftrace backend の実装には ftrace marker が用いられています。ftrace marker は debugfs の marker file への書き込みを ftrace のリングバッファに送る機能です。Qemu ftrace backend は、Qemu のトレース情報出力先を marker file にすることで実現しています。
関連コミット:
trace: Add ftrace tracing backend
2013年5月3日に Qemu Mainline にマージされました。おそらく Qemu 1.5 で使用できるはずです。Author の名前が自分と酷似していますが。。。
使い方
ftrace backend を使うためには、まず configure 時に trace backend として "ftrace" を指定する必要があります。
# ./configure --trace-backend=ftrace
このままでも ftrace backend はトレース情報を debugfs の trace file に記録できますが、今回は KVM を使うのでKVM 関係のトレースイベント情報も併せて取得するよう設定します。
# echo 1 > /sys/kernel/debug/tracing/events/kvm/enable
Qemu 起動時に、取得対象の Qemu trace イベントを指定します。
Qemu のトレースイベント一覧は Qemu ソースコードの trace-events ファイルに記載されています。また、 Qemu monitor から "info trace-events" コマンドによっても取得できます。
ここでは、すべてのイベントを取得するような設定にします。
% cat /home/eiichi/events *Qemu を起動します。ftrace を使うため、必ず root 権限で起動しましょう。
# ./qemu-system-x86_64 -enable-kvm -trace events=/home/eiichi/events
得られるトレース出力は以下のようになります。
# less /sys/kernel/debug/tracing/trace snip... qemu-system-x86-23226 [002] d... 116142.685922: kvm_entry: vcpu 0 qemu-system-x86-23226 [002] d... 116142.685923: kvm_exit: reason IO_INSTRUCTION rip 0xc45b info 700040 0 qemu-system-x86-23226 [002] .... 116142.685924: kvm_pio: pio_write at 0x70 size 1 count 1 qemu-system-x86-23226 [002] .... 116142.685925: kvm_userspace_exit: reason KVM_EXIT_IO (2) qemu-system-x86-23226 [002] ...1 116142.685943: tracing_mark_write: cpu_set_apic_base 00000000fee00900 qemu-system-x86-23226 [002] ...1 116142.685946: tracing_mark_write: kvm_run_exit cpu_index 0, reason 2 qemu-system-x86-23226 [002] ...1 116142.685947: tracing_mark_write: cpu_out addr 0x70 value 143 qemu-system-x86-23226 [002] ...1 116142.685951: tracing_mark_write: kvm_vcpu_ioctl cpu_index 0, type 44672, arg (nil) qemu-system-x86-23226 [002] d... 116142.685954: kvm_entry: vcpu 0 snip...tracing_mark_write と書かれているのが、Qemu(ユーザ空間)のトレース情報です。ここでは、ゲストCPUのIO port write命令を受けて VM_EXIT(reason KVM_EXIT_IO) したのちユーザ空間でエミュレーションが行われているのがわかります。
libvirt で使う場合
libvirt で ftrace backend を使う場合は、libvirtd の設定ファイルを以下のように変更し、Qemu を起動するユーザを root にする必要があります。
/etc/libvirt/qemu.conf に以下を追加:
user = "root"
余談
ftrace marker を使うためのライブラリがあれば、もっとftrace backend のコードを短くできますし、他のアプリケーションでも手軽に ftrace marker が使えるようになって便利です。ライブラリ名は、"libftrace" などという名前になるのでしょうか。
最近、ftrace は snapshot 機能や、multiple buffer 機能が追加されています。ユーザ空間のプログラム内からそれらの機能を利用するライブラリがあるとさらに嬉しいです。
2013/03/24
ftrace marker と uprobe-based event tracer オーバヘッド比較
はじめに
ユーザ空間のプログラムのトレース情報を取得する方法には、さまざまなものがありますが、ここでは "ftrace marker" と "uprobe-based event tracer" を取り上げ、トレースオーバヘッドを測定してみます。
"ftrace marker" とは ftrace trace_marker ファイルにトレース情報を書き込むことで、ftrace でトレース情報を取得する機能です。Kernel のトレース情報とユーザプログラムのトレース情報を照らしあわせたい時等に便利です。
使い方は簡単で、tracing ディレクトリの trace_marker ファイルに write するだけです。
例
"uprobe-based event tracer" とは kprobe-based event tracer のユーザ空間版で、kprobe のように動的にプローブを差し込むことができる機能です。以下でも若干解説しますが、詳しい使い方については、ドキュメントをご覧ください。
比較方法
"ftrace marker" と "uprobe-based event tracer" をそれぞれ使って、トレースポイントを 1000回踏むのにかかる時間を測定します。
測定プログラムは以下を用意しました。
下の3つの関数をそれぞれ 1000回ずつ呼び出し、かかった時間を測定する簡単なものです。
do_nothing() : 関数呼び出しのオーバヘッドを考慮するための何もしない関数
marker_write() : ftrace marker に write する関数
uprobe_test() : uprobe を差し込む対象の関数
probench.c
上記のプログラムを make したあと、uprobe を差し込む場所を指定します。
まず、ELF を読んでプログラムヘッダからテキストセグメント開始アドレスを探しましょう。
次に、uprobe_test 関数のアドレスは以下のコマンドで取得できます。
uprobe_test にプローブを差し込みましょう。uprobe_test 関数のアドレス0x400830 から テキストセグメント開始アドレス 0x400000を引き算した 0x830 をuprobe_events に書き込みます。書き込み後はイベントをオンにしてやります。
結果は、
ftrace marker : 1.7us/TracePoint
uprobe : 4.3us/TracePoint
でした。関数呼び出しのオーバヘッドは無視できるようです。
uprobe は ftrace marker と比べて、若干オーバヘッドが嵩むようです。
余談
uprobe-based event tracer ではなく、systemtap を使った場合でも測って見ました。
以下のようなスクリプトを実行したあとで probench を走らせます。
まとめ
"ftrace marker" と "uprobe-based event tracer" のオーバヘッドを比較すると、前者より後者のほうが2倍以上大きいことがわかりました。トレースオーバヘッドを気にする かつ ユーザプログラムを改変できる場合は ftrace marker を使ったほうがいいでしょう。
参考情報:
kernel/Documentation/trace/ftrace.txt
kernel/Documentation/trace/kprobetracer.txt
kernel/Documentation/trace/uprobetracer.txt
ユーザ空間のプログラムのトレース情報を取得する方法には、さまざまなものがありますが、ここでは "ftrace marker" と "uprobe-based event tracer" を取り上げ、トレースオーバヘッドを測定してみます。
"ftrace marker" とは ftrace trace_marker ファイルにトレース情報を書き込むことで、ftrace でトレース情報を取得する機能です。Kernel のトレース情報とユーザプログラムのトレース情報を照らしあわせたい時等に便利です。
使い方は簡単で、tracing ディレクトリの trace_marker ファイルに write するだけです。
例
# echo test > /sys/kernel/debug/tracing/ftrace_marker
# cat /sys/kernel/debug/tracing/trace
# tracer: nop
#
# entries-in-buffer/entries-written: 1/1 #P:4
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
<...>-23108 [003] ...1 34500.124363: tracing_mark_write: test
"uprobe-based event tracer" とは kprobe-based event tracer のユーザ空間版で、kprobe のように動的にプローブを差し込むことができる機能です。以下でも若干解説しますが、詳しい使い方については、ドキュメントをご覧ください。
比較方法
"ftrace marker" と "uprobe-based event tracer" をそれぞれ使って、トレースポイントを 1000回踏むのにかかる時間を測定します。
測定プログラムは以下を用意しました。
下の3つの関数をそれぞれ 1000回ずつ呼び出し、かかった時間を測定する簡単なものです。
do_nothing() : 関数呼び出しのオーバヘッドを考慮するための何もしない関数
marker_write() : ftrace marker に write する関数
uprobe_test() : uprobe を差し込む対象の関数
probench.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/time.h>
int marker_fd;
long time_usec(void) {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000000 + tv.tv_usec;
}
void probe_bench(void (*func)()) {
long start_us, end_us, diff_us;
int i, loop_max;
loop_max = 1000;
start_us = time_usec();
for (i = 0; i < loop_max; i++) {
func();
}
end_us = time_usec();
diff_us = end_us - start_us;
printf("Elapsed time(us): %ld\n", diff_us);
printf("per trace point(us): %f\n", (double) diff_us / loop_max);
printf("\n");
}
void do_nothing(void) {
}
void marker_write(void) {
write(marker_fd, "test\n", 5);
}
void uprobe_test(void) {
}
int main(int argc, char *argv)
{
char path[256];
int trace_fd;
strcpy(path, "/sys/kernel/debug/tracing/tracing_on");
trace_fd = open(path, O_WRONLY);
if (trace_fd >= 0)
write(trace_fd, "1", 1);
strcpy(path, "/sys/kernel/debug/tracing/trace_marker");
marker_fd = open(path, O_WRONLY);
printf("-- nothing --\n");
probe_bench(do_nothing);
printf("-- ftrace marker --\n");
probe_bench(marker_write);
printf("-- uprobe --\n");
probe_bench(uprobe_test);
close(marker_fd);
}
Makefile
probench: probench.c gcc -g -o $@ $< -O2 clean: rm probench
上記のプログラムを make したあと、uprobe を差し込む場所を指定します。
まず、ELF を読んでプログラムヘッダからテキストセグメント開始アドレスを探しましょう。
# readelf -e probench | less
...
プログラムヘッダ:
タイプ オフセット 仮想Addr 物理Addr
ファイルサイズ メモリサイズ フラグ 整列
...
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000000be4 0x0000000000000be4 R E 200000
テキストセグメント開始アドレスは0x400000とわかりました。次に、uprobe_test 関数のアドレスは以下のコマンドで取得できます。
# readelf -s probench | grep uprobe_test
59: 0000000000400830 2 FUNC GLOBAL DEFAULT 13 uprobe_test
アドレスは0x400830とわかりました。uprobe_test にプローブを差し込みましょう。uprobe_test 関数のアドレス0x400830 から テキストセグメント開始アドレス 0x400000を引き算した 0x830 をuprobe_events に書き込みます。書き込み後はイベントをオンにしてやります。
# echo 'p:test /path/to/probench:0x830' > /sys/kerenel/debug/tracing/uprobe_events # echo 1 > /sys/kerenel/debug/tracing/events/uprobes/test/enable以上で準備がととのいました。ベンチマークプログラム : probench を実行しましょう。
# ./probench -- nothing -- Elapsed time(us): 8 per trace point(us): 0.008000 -- ftrace marker -- Elapsed time(us): 1742 per trace point(us): 1.742000 -- uprobe -- Elapsed time(us): 4255 per trace point(us): 4.255000
結果は、
ftrace marker : 1.7us/TracePoint
uprobe : 4.3us/TracePoint
でした。関数呼び出しのオーバヘッドは無視できるようです。
uprobe は ftrace marker と比べて、若干オーバヘッドが嵩むようです。
余談
uprobe-based event tracer ではなく、systemtap を使った場合でも測って見ました。
以下のようなスクリプトを実行したあとで probench を走らせます。
#!/usr/bin/stap
probe begin {
printf("start\n")
}
probe process("/home/eiichi/git/probench/probench").function("uprobe_test") {
}
# ./probench -- nothing -- Elapsed time(us): 8 per trace point(us): 0.008000 -- ftrace marker -- Elapsed time(us): 1766 per trace point(us): 1.766000 -- uprobe -- Elapsed time(us): 3994 per trace point(us): 3.994000systemtap の場合は 4.0us/TracePoint でした。uprobe-based event tracer と変わらないようです。
まとめ
"ftrace marker" と "uprobe-based event tracer" のオーバヘッドを比較すると、前者より後者のほうが2倍以上大きいことがわかりました。トレースオーバヘッドを気にする かつ ユーザプログラムを改変できる場合は ftrace marker を使ったほうがいいでしょう。
参考情報:
kernel/Documentation/trace/ftrace.txt
kernel/Documentation/trace/kprobetracer.txt
kernel/Documentation/trace/uprobetracer.txt
2013/03/11
Netperf でネットワークレイテンシ測定
Netperf は iperf と並ぶ多機能ネットワークベンチマークソフトです。
Netperf を使って latency(RTT: Round Trip Time) を測定する方法についてまとめました。
omni-test による latency 測定
最近の netperf は "omni-test" による測定を行うのが一般的のようです。
omni test で latency を測定しましょう。
ここで、latency とは送信サーバと受信サーバでパケットが往復するのにかかる時間(RTT)のことを言うとします。 まずは受信側で受信準備します。
-t : テストモード指定。ここでは omni テスト。
-H : 受信サーバのホスト名またはIP addressを指定。
-- : 以降が omni テストのためのオプションになります。
-d : direction 指定。rr は request/response = 往復時間測定。
-T : プロトコルを指定。ここでは UDP。他には TCP, SCTP も指定可能。
-k : omni test 特有の出力項目セレクタ。
出力は レイテンシの最小値、平均値、90%分位点、99%分位点、最大値、標準偏差、などが出力可能です。
-k オプションで omni test で出力する項目を指定する項目は非常にたくさんあります。
-k all とすると、すべて出力されるようになります。測定ログとして残すには十分でしょう。
ヒストグラム出力
レイテンシの測定では、分位点などの要約統計量だけでなくヒストグラムが欲しいことがあります。
netperf でヒストグラムを出力する際は、コンパイルする際に、configure に --enable-histogram オプションを渡します。
余談
上記の測定には、対象として自身(localhost)を指定していました。
レイテンシの最大値: 1.4 ms という数字は、ping コマンドで RTT を測定した場合に比べて大きいです。これは、Kernel 内でパケットが折り返す ping(ICMP) とは違い、ユーザ空間までのパケット往復時間で図っているためです。 パケットがユーザ空間まで上がる分、スケジュールの関係で netperf や netserver プロセスの実行が遅れてしまうことがあるからです。
参考文献
Care and Feeding of Netperf
omni-test による latency 測定
最近の netperf は "omni-test" による測定を行うのが一般的のようです。
omni test で latency を測定しましょう。
ここで、latency とは送信サーバと受信サーバでパケットが往復するのにかかる時間(RTT)のことを言うとします。 まずは受信側で受信準備します。
# netserver送信側で UDP で RTT を測定するコマンドを発行します。
# netperf -t omni -H localhost -- -d rr -T UDP -k MIN_LATENCY,MEAN_LATENCY,P90_LATENCY,P99_LATENCY,MAX_LATENCY,STDDEV_LATENCY OMNI Send|Recv TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to localhost.localdomain () port 0 AF_INET MIN_LATENCY=11 MEAN_LATENCY=44.34 P90_LATENCY=50 P99_LATENCY=74 MAX_LATENCY=3079 STDDEV_LATENCY=13.49latency の単位は micro second です。 オプションについて簡単に説明します。
-t : テストモード指定。ここでは omni テスト。
-H : 受信サーバのホスト名またはIP addressを指定。
-- : 以降が omni テストのためのオプションになります。
-d : direction 指定。rr は request/response = 往復時間測定。
-T : プロトコルを指定。ここでは UDP。他には TCP, SCTP も指定可能。
-k : omni test 特有の出力項目セレクタ。
出力は レイテンシの最小値、平均値、90%分位点、99%分位点、最大値、標準偏差、などが出力可能です。
-k オプションで omni test で出力する項目を指定する項目は非常にたくさんあります。
-k all とすると、すべて出力されるようになります。測定ログとして残すには十分でしょう。
# netperf -t omni -H localhost -- -d rr -T UDP -k all OMNI Send|Recv TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to localhost.localdomain () port 0 AF_INET SOCKET_TYPE=Stream PROTOCOL=UDP DIRECTION=Send|Recv ELAPSED_TIME=10.00 THROUGHPUT=22333.41 THROUGHPUT_UNITS=Trans/s ... UUID=46412b12-8a4a-11e2-985e-e91ceb3a7ad7 MIN_LATENCY=11 MAX_LATENCY=2478 P50_LATENCY=44 P90_LATENCY=52 P99_LATENCY=77 MEAN_LATENCY=43.90 STDDEV_LATENCY=15.74 LOCAL_SOCKET_PRIO=-1 REMOTE_SOCKET_PRIO=-1 LOCAL_SOCKET_TOS=0xffffffff REMOTE_SOCKET_TOS=0xffffffff LOCAL_CONG_CONTROL= REMOTE_CONG_CONTROL= LOCAL_FILL_FILE= REMOTE_FILL_FILE= COMMAND_LINE="netperf -t omni -H localhost -- -d rr -T UDP -k all"
ヒストグラム出力
レイテンシの測定では、分位点などの要約統計量だけでなくヒストグラムが欲しいことがあります。
netperf でヒストグラムを出力する際は、コンパイルする際に、configure に --enable-histogram オプションを渡します。
# ./configure --enable-histogram測定の際には -v 2 オプションを付け、 verbosity を 1 より大きくします。
# netperf -t omni -H localhost -v 2 -- -d rr -T UDP -k MIN_LATENCY,MEAN_LATENCY,P90_LATENCY,P99_LATENCY,MAX_LATENCY,STDDEV_LATENCY OMNI Send|Recv TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to localhost.localdomain () port 0 AF_INET : histogram MIN_LATENCY=11 MEAN_LATENCY=44.87 P90_LATENCY=46 P99_LATENCY=71 MAX_LATENCY=1447 STDDEV_LATENCY=7.58 Histogram of request/response times UNIT_USEC : 0: 0: 0: 0: 0: 0: 0: 0: 0: 0 TEN_USEC : 0: 3031: 1094: 413: 197318: 9043: 4774: 1942: 441: 135 HUNDRED_USEC : 0: 163: 4: 2: 3: 0: 1: 0: 0: 0 UNIT_MSEC : 0: 1: 0: 0: 0: 0: 0: 0: 0: 0 TEN_MSEC : 0: 0: 0: 0: 0: 0: 0: 0: 0: 0 HUNDRED_MSEC : 0: 0: 0: 0: 0: 0: 0: 0: 0: 0 UNIT_SEC : 0: 0: 0: 0: 0: 0: 0: 0: 0: 0 TEN_SEC : 0: 0: 0: 0: 0: 0: 0: 0: 0: 0 >100_SECS: 0 HIST_TOTAL: 218365
余談
上記の測定には、対象として自身(localhost)を指定していました。
レイテンシの最大値: 1.4 ms という数字は、ping コマンドで RTT を測定した場合に比べて大きいです。これは、Kernel 内でパケットが折り返す ping(ICMP) とは違い、ユーザ空間までのパケット往復時間で図っているためです。 パケットがユーザ空間まで上がる分、スケジュールの関係で netperf や netserver プロセスの実行が遅れてしまうことがあるからです。
参考文献
Care and Feeding of Netperf
2013/03/09
Qemu ゲストからの virtio-blk cache 設定確認
Qemu/KVM を用いた VPS(IaaS) を使っていると、Qemu virtio-blk Disk の cache 設定がどうなっているのか気になることがあります。実は、ゲストから以下のコマンドで cache 設定を確認できます。
(virtioXの数字Xはlspciコマンドなどで確認してください。)
(Qemu cache mode は "directsync", "writethrough" のどちらか)、
"write back" ならば Disk Write Cache あり
("none", "writeback", "unsafe" のどれか)です。
Qemu の cache mode について詳しくは、拙稿 Qemu cache mode まとめをご覧ください。
ゲストの kernel バージョンによっては上記の cache_type が用意されていないこともあります。そのときは以下のコマンドで virtio-blk デバイスの feature bits を参照することで確認できます。左から10番目が disk cache の有無を示す bit です。 1 なら有り、0なら無しです。
ちなみに、自分が借りている、"さくらのVPS"での feature bits は以下でした。
自分のローカル環境との違いは SCSI コマンドサポートの有無と、disk cache の動的変更の可否です。
他の Feature bits に関する情報は qemu の hw/virtio-blk.h または Linux カーネルの virtio-blk デバイスドライバ drivers/block/virtio_blk.c から手に入ります。
(virtioXの数字Xはlspciコマンドなどで確認してください。)
[eiichi@f1 vda]$ cat /sys/bus/virtio/devices/virtio1/block/vda/cache_type write back"write through" ならば Disk Write Cache なし
(Qemu cache mode は "directsync", "writethrough" のどちらか)、
"write back" ならば Disk Write Cache あり
("none", "writeback", "unsafe" のどれか)です。
Qemu の cache mode について詳しくは、拙稿 Qemu cache mode まとめをご覧ください。
ゲストの kernel バージョンによっては上記の cache_type が用意されていないこともあります。そのときは以下のコマンドで virtio-blk デバイスの feature bits を参照することで確認できます。左から10番目が disk cache の有無を示す bit です。 1 なら有り、0なら無しです。
[eiichi@f1]~% cat /sys/bus/virtio/devices/virtio1/features 0010101101110000000000000000110000000000000000000000000000000000この例では左から10番目が1なので disk cache "有り"です。
ちなみに、自分が借りている、"さくらのVPS"での feature bits は以下でした。
0010101001100000000000000000110000000000000000000000000000000000さくらのVPSも disk cache 有りのようです。
自分のローカル環境との違いは SCSI コマンドサポートの有無と、disk cache の動的変更の可否です。
他の Feature bits に関する情報は qemu の hw/virtio-blk.h または Linux カーネルの virtio-blk デバイスドライバ drivers/block/virtio_blk.c から手に入ります。
/* Feature bits */ #define VIRTIO_BLK_F_BARRIER 0 /* Does host support barriers? */ #define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */ #define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */ #define VIRTIO_BLK_F_GEOMETRY 4 /* Indicates support of legacy geometry */ #define VIRTIO_BLK_F_RO 5 /* Disk is read-only */ #define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/ #define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ /* #define VIRTIO_BLK_F_IDENTIFY 8 ATA IDENTIFY supported, DEPRECATED */ #define VIRTIO_BLK_F_WCE 9 /* write cache enabled */ #define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ #define VIRTIO_BLK_F_CONFIG_WCE 11 /* write cache configurable */ #define VIRTIO_BLK_ID_BYTES 20 /* ID string length */新しく Qemu/KVM で構築された VPS などを借りてみたときは virtio-blk cache 設定を確認されてはいかがでしょうか。
systemtap で KVM ゲストとホストのTSC値を一致させる
はじめに
KVM でゲストを作成する際、KVMはご丁寧にもゲスト作成時の TSC をゼロから数えるためにホストのTSCからのズレ(オフセット)を用意します。ゲスト内で RDTSC 命令が発行されると、ホストのTSCにゲストのTSC offset を足したものを取得します。
ところで、ゲストからみたTSC値がホストからみたTSC値と異なると、ゲストの動作記録とQemuやホストOSの動作記録を突き合わせるときに、ゲストのTSC offsetを考慮しなければならず面倒です。そこで、systemtap を使ってカーネル無改造で手軽に TSC offset をゼロにしようと言うわけです。TSC offset が 0 になれば、ゲストとホストから見える TSC値は一致します。
TSC offset ゼロ化
KVM は ioctl KVM_CREATE_VCPU が発行されると、Virtual CPU を作成します。この際、現時点でのTSC値を取得し、その負値を tsc_offset にします。(vmx_write_tsc_offset@arch/x86/kvm/vmx.c)
vmx_write_tsc_offst 関数に systemtap の probe を差し込んで、guru モードで $tsc_offset = 0に書き換えてやればいいわけです。
systemtap の '-L' オプションで、probe を差し込む箇所を確認します。
以下のような stap script を用意します。
上のスクリプトを実行し、start が表示されたらゲストを起動します。
動作例はこんな感じ:
これでゲスト内で RDTSC によって取得する TSC値はホストと同じものになります。ゲストとホストでトレースを突き合わせる場合は、例えば systemtap なら get_cycles() でタイムスタンプを取得してゲストホストそれぞれでトレースデータを保存し、あとでマージします。
一度ゲストが起動してしまえば、ゲスト内で TSC書き換えが起こらない限り、上記の stap script を停止してしまっても大丈夫です。
Systemtap を使えば、ゲストOS-Qemu-ホストOS にまたがる動作記録を一貫して取得できるので大変便利です。Qemu/KVMの調査がはかどります。
KVM でゲストを作成する際、KVMはご丁寧にもゲスト作成時の TSC をゼロから数えるためにホストのTSCからのズレ(オフセット)を用意します。ゲスト内で RDTSC 命令が発行されると、ホストのTSCにゲストのTSC offset を足したものを取得します。
ところで、ゲストからみたTSC値がホストからみたTSC値と異なると、ゲストの動作記録とQemuやホストOSの動作記録を突き合わせるときに、ゲストのTSC offsetを考慮しなければならず面倒です。そこで、systemtap を使ってカーネル無改造で手軽に TSC offset をゼロにしようと言うわけです。TSC offset が 0 になれば、ゲストとホストから見える TSC値は一致します。
TSC offset ゼロ化
KVM は ioctl KVM_CREATE_VCPU が発行されると、Virtual CPU を作成します。この際、現時点でのTSC値を取得し、その負値を tsc_offset にします。(vmx_write_tsc_offset@arch/x86/kvm/vmx.c)
vmx_write_tsc_offst 関数に systemtap の probe を差し込んで、guru モードで $tsc_offset = 0に書き換えてやればいいわけです。
systemtap の '-L' オプションで、probe を差し込む箇所を確認します。
[root@edge2]/home/eiichi/stap# stap -L 'module("kvm_intel").statement("vmx_write_tsc_offset")'
module("kvm_intel").statement("vmx_write_tsc_offset@arch/x86/kvm/vmx.c:1872")
さらに local 変数を表示させます。[root@edge2]/home/eiichi/stap# stap -L 'module("kvm_intel").statement("vmx_write_tsc_offset@arch/x86/kvm/vmx.c:1872")'
module("kvm_intel").statement("vmx_write_tsc_offset@arch/x86/kvm/vmx.c:1873") $vcpu:struct kvm_vcpu* $offset:u64
$offset で tsc offset が取得できます。
以下のような stap script を用意します。
[root@edge2]/home/eiichi/stap# cat tsc_offset_zero.stp
#!/usr/bin/stap -g
probe begin { printf("start\n") }
probe module("kvm_intel").statement("vmx_write_tsc_offset@arch/x86/kvm/vmx.c:1872"){
$offset = 0
printf("VCPU : %x tsc offset changed to 0\n", $vcpu)
print_backtrace()
}
実行するときには、stap -g オプションをつけましょう。'-g' オプションは guru モードといって、カーネル内変数を上書きできる素敵なモードです。上のスクリプトを実行し、start が表示されたらゲストを起動します。
動作例はこんな感じ:
[root@edge2]/home/eiichi/stap# stap -g -d kernel -d kvm tsc_offset_zero.stp start VCPU : ffff88004ee23dd0 tsc offset changed to 0 0xffffffffa022f9ad : vmx_write_tsc_offset+0xd/0x50 [kvm_intel] 0xffffffffa01badcd : kvm_write_tsc+0x18d/0x2c0 [kvm] 0xffffffffa0232c26 : vmx_create_vcpu+0x386/0x810 [kvm_intel] 0xffffffffa01c43b7 : kvm_arch_vcpu_create+0x47/0x70 [kvm] 0xffffffffa01aeb0e : kvm_vm_ioctl+0x2ae/0x5b0 [kvm] 0xffffffff811a6529 : do_vfs_ioctl+0x99/0x580 [kernel] 0xffffffff811a6aa1 : sys_ioctl+0x91/0xb0 [kernel] 0xffffffff8163f9d9 : system_call_fastpath+0x16/0x1b [kernel] VCPU : ffff88004ee23dd0 tsc offset changed to 0 0xffffffffa022f9ad : vmx_write_tsc_offset+0xd/0x50 [kvm_intel] 0xffffffffa01badcd : kvm_write_tsc+0x18d/0x2c0 [kvm] 0xffffffffa0231f29 : vmx_set_msr+0x179/0x2a0 [kvm_intel] 0xffffffffa01b4f29 : do_set_msr+0x19/0x20 [kvm] 0xffffffffa01bcf60 : msr_io+0xc0/0x150 [kvm] 0xffffffffa01c039b : kvm_arch_vcpu_ioctl+0x59b/0xf50 [kvm] 0xffffffffa01acea8 : kvm_vcpu_ioctl+0x118/0x610 [kvm] 0xffffffff811a6529 : do_vfs_ioctl+0x99/0x580 [kernel] 0xffffffff811a6aa1 : sys_ioctl+0x91/0xb0 [kernel] 0xffffffff8163f9d9 : system_call_fastpath+0x16/0x1b [kernel]
これでゲスト内で RDTSC によって取得する TSC値はホストと同じものになります。ゲストとホストでトレースを突き合わせる場合は、例えば systemtap なら get_cycles() でタイムスタンプを取得してゲストホストそれぞれでトレースデータを保存し、あとでマージします。
一度ゲストが起動してしまえば、ゲスト内で TSC書き換えが起こらない限り、上記の stap script を停止してしまっても大丈夫です。
Systemtap を使えば、ゲストOS-Qemu-ホストOS にまたがる動作記録を一貫して取得できるので大変便利です。Qemu/KVMの調査がはかどります。
2013/02/27
qemu VNC on WebSocket
はじめに
qemu 1.4 では Websocket プロトコル上で VNC を実現する機能が実装されました。
Qemu/Changelog/1.4
従来、ブラウザ(Websocket)でVNCを利用する場合、Websockify などを用いて、Websocket 通信を通常のソケット通信に変換しなければなりませんでした。今回 qemu VNC がWebsocket 対応したことにより、従来のような面倒な変換なしにブラウザ経由で VNC を利用できます。HTML5対応のブラウザであれば、Java プラグインなど無しに気軽に VNC を利用できるようになるのでとても便利です。
関連コミット:
http://git.qemu.org/?p=qemu.git;a=commit;h=7536ee4bc3da7e9b7fdadba5ba6ade63eaace430
使い方
qemu の configure 段階で vnc-ws を有効にします。
コンパイルした qemu を使用するため、emulator タグで qemu の path を変更します。
また、qemu に "-vnc :1,websocket" オプションを渡すためのタグを追加します。ここで、"1:" はディスプレイポート番号です。"0:" だと既存のディスプレイと重なるかもしれないので、ずらしてあります。
仮想マシンを起動し、novnc.com にアクセスします。
noVNC.com は HTML5(Canvasなど) と Websocket で実装された、Web ベースの VNC クライアントです。 ブラウザは必ず HTML5 対応の新し目のブラウザを使いましょう。
右上の "Connect" ボタンをクリックし、
Host : 127.0.0.1
Port : 5701
を入力します。ポート番号は、上記で設定したディスプレイ番号に 5700 を足したものになります。今の場合だと、ディスプレイ番号 "1" + 5700 = 5701 です。
接続できると、こんな感じで、画面が表示されます。Chrome の中で Firefox を動かして見ました。
せっかくなので、iphone と Nexus 7 でも試して見ました。
Nexus 7
iphone
自宅の WiMAX環境では、iphone は重くて使い物になりませんでした。Nexus 7 もキーボードがいまいち使いにくかったです。
ともあれ、VNC on Websocket ではブラウザさえあれば、どこからでも VNC で画面転送できるのは便利です。家の仮想マシンを VPN 接続で iphone から操作する、なんてもこともできます。
余談
SPICE の HTML5 クライアントはないのかな?と思ったらありました。
HTML5 - SPICE
qemu が Spice on Websocket に対応していないので、 Websockify が必要です。
qemu 1.4 では Websocket プロトコル上で VNC を実現する機能が実装されました。
Qemu/Changelog/1.4
従来、ブラウザ(Websocket)でVNCを利用する場合、Websockify などを用いて、Websocket 通信を通常のソケット通信に変換しなければなりませんでした。今回 qemu VNC がWebsocket 対応したことにより、従来のような面倒な変換なしにブラウザ経由で VNC を利用できます。HTML5対応のブラウザであれば、Java プラグインなど無しに気軽に VNC を利用できるようになるのでとても便利です。
関連コミット:
http://git.qemu.org/?p=qemu.git;a=commit;h=7536ee4bc3da7e9b7fdadba5ba6ade63eaace430
使い方
qemu の configure 段階で vnc-ws を有効にします。
$ ./configure --enable-vnc-ws以下のようなエラーがでるかもしれません。
ERROR ERROR: User requested feature vnc-ws ERROR: configure was not able to find it ERRORその時は、gnutls-devel パッケージをインストールします。
# yum install gnutls-devel無事にインストールが済んだら、libvirt XML ファイルを編集しましょう。
コンパイルした qemu を使用するため、emulator タグで qemu の path を変更します。
また、qemu に "-vnc :1,websocket" オプションを渡すためのタグを追加します。ここで、"1:" はディスプレイポート番号です。"0:" だと既存のディスプレイと重なるかもしれないので、ずらしてあります。
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
...
<devices>
<emulator>/usr/local/bin/qemu-system-x86_64</emulator>
...
</devices>
<qemu:commandline>
<qemu:arg value='-vnc'/>
<qemu:arg value=':1,websocket'/>
</qemu:commandline>
</domain>
仮想マシンを起動し、novnc.com にアクセスします。
noVNC.com は HTML5(Canvasなど) と Websocket で実装された、Web ベースの VNC クライアントです。 ブラウザは必ず HTML5 対応の新し目のブラウザを使いましょう。
右上の "Connect" ボタンをクリックし、
Host : 127.0.0.1
Port : 5701
を入力します。ポート番号は、上記で設定したディスプレイ番号に 5700 を足したものになります。今の場合だと、ディスプレイ番号 "1" + 5700 = 5701 です。
接続できると、こんな感じで、画面が表示されます。Chrome の中で Firefox を動かして見ました。
せっかくなので、iphone と Nexus 7 でも試して見ました。
Nexus 7
iphone
自宅の WiMAX環境では、iphone は重くて使い物になりませんでした。Nexus 7 もキーボードがいまいち使いにくかったです。
ともあれ、VNC on Websocket ではブラウザさえあれば、どこからでも VNC で画面転送できるのは便利です。家の仮想マシンを VPN 接続で iphone から操作する、なんてもこともできます。
余談
SPICE の HTML5 クライアントはないのかな?と思ったらありました。
HTML5 - SPICE
qemu が Spice on Websocket に対応していないので、 Websockify が必要です。
登録:
投稿 (Atom)


