ラベル qemu の投稿を表示しています。 すべての投稿を表示
ラベル qemu の投稿を表示しています。 すべての投稿を表示

2013/10/12

Virsh で Qemu/KVM Live Block Migration

はじめに

仮想環境での Live Migration というと、仮想マシン移行元と移行先ホストでディスクを共有した上で行うのが一般的です。Live Block Migration は、共有ディスクが無い場合でも、仮想ストレージを移行させることにより Live Migration を実現する技術です。VMWare においては、Storage vMotion と呼ばれています。今回は、Qemu/KVM 環境において virsh を使った Live Block Migration の使い方をご紹介します。検証環境は Fedora 19です。

Live Block Migration には、仮想マシンの仮想ストレージすべてをコピーする Full モードと、Backing File との差分のみコピーする Incremental モードがあります。下記でそれぞれを紹介します。

Live Block Migration の使い方(Full編)

Live Block Migration を行う前に、前準備として移行先において仮想ストレージのスタブを準備する必要があります。(libvirt-devel に事前にスタブを作成するパッチが投稿されているため、この作業は必要なくなるかもしれません。パッチ: Pre-create storage on live migration)
まず、移行元ディスクの容量を調べます。ここではQcow2フォーマットを用いています。
[@src] # qemu-img info f19.img
image: f19.img
file format: qcow2
virtual size: 49G (52428800000 bytes)
disk size: 4.9G
cluster_size: 65536

移行先にて、スタブを作成します。移行元と同じパスに、移行元のディスク容量と同じ容量のスタブを作成します。
[@dst] # qemu-img create -f qcow2 f19.img 52428800000

移行元にて、virsh コマンドでマイグレーションを開始します。通常の Live Migrationでのオプションに加え、--copy-storage-all をつけることにより、Block Migration になります。
[@src] # virsh migrate --live --verbose --copy-storage-all f19 qemu+ssh://dst-ip/system
デフォルトでは port : 45192 を使うので、開けておきましょう。
すべてのストレージをコピーするため、マイクレーションには結構(数分)時間がかかります。
マイグレーションが完了したら、移行先で仮想マシンが稼働していることを確認しましょう。

Live Block Migration の使い方(Incremental編)

仮想ストレージ全てをコピーする Full モードは、かなり時間がかかってしまうという問題があります。Qemu/KVM には、事前にベースとなる仮想ストレージ(backing)を作成し、それとの差分のみを記録する、スナップショット機能があります。この機能との組みあせで Live Block Migration を行うと、backing との差分のみがコピーされるため、マイグレーション時間を短縮できます。

スナップショットの作成:
仮想マシンが稼働していない状態で行います。
[@src] # qemu-img create -f qcow2 -b base.img migrate-inc.img
-b でベースとなる backing file を指定し、migrate-inc.img を作成しました。
移行先でも同じコマンドでスタブを作成しておきます。
[@dst] # qemu-img create -f qcow2 -b base.img migrate-inc.img

移行元で、仮想ストレージに migrate-inc.img を指定した仮想マシンを作成し、起動しておきます。(説明略)

virsh コマンドで Incremental モードでの Live Block Migration を行います。
[@src] # virsh migrate --live --verbose --copy-storage-inc vm-name qemu+ssh://dst-ip/system
Fullモードとは違い、--copy-storage-inc オプションを使います。
backing との差分のみをコピーするので、Fullと比較して短い時間で完了します。

付録

Qemu の Live Block Migration はQemu 0.12 の頃から QMP コマンドの migrate -b が使われていましたが、新しめの Qemu(1.3 以降)では nbd と drive-mirror コマンドが使われるようになっています。参考:Qemu Wiki: Features/Virt Storage Migration
libvirt では Migration API がバージョン3まであり、Qemu が対応している場合は、新しい方法(nbd + drive-mirror)でマイグレーションし、対応していない場合は以前の方法にフォールバックするようになっています。参考:libvirt : virDomainMigrate3
nbd と drive-mirror による Live Block Migration については、以下のパッチに詳しい説明があります。
[libvirt] [PATCH v3 00/12 ] Rework Storage Migration

参考文献

Qemu Wiki: Features-Old/LiveBlockMigration
Qemu Wiki: Features/Virt Storage Migration
Qemu Wiki: Features-Done/ImageStreamingAPI
[libvirt] [PATCH v3 00/12 ] Rework Storage Migration
libvirt : virDomainMigrate3

2013/09/28

Qemu/KVM で CPU Hotplug を使う

はじめに

Hotplug とはマシンを停止せずにCPU、メモリなどのデバイスを追加する技術です。CPU Hotplug を仮想環境で用いると、仮想マシンを停止することなく仮想CPUを追加し、処理能力を強化することができます。これにより、仮想マシンの無停止スケールアップを実現できます。
Qemuはversion1.5よりCPU Hotplug機能をサポートしています。今回はQemuでのCPU Hotplugの使い方についてご紹介します。
検証環境はFedora19です。

Qemu のコンパイル

Hotplugのサポートは1.5以降です。Qemuのversionが1.5未満の場合は最新のQemuをコンパイルしましょう。
# git clone git://git.qemu.org/qemu.git
# cd qemu
# ./configure --target-list=x86_64-softmmu
# make
# make install

CPU Hotplugの使い方は、複数あります。以下では、QMPを用いる方法とlibvirt(virsh, virt-manager)経由でCPU Hotplugする方法を記載します。

共通の前提

CPU Hotplug機能を使うためには、あらかじめ、Qemu を起動する時のパラメータ: maxcpus を2以上にしておく必要があります。CPU Hotplug可能な数の上限は maxcpus となります。例えば、
qemu ... -smp 1,maxcpus=4
といった具合です。virsh では  vcpu タグの要素が maxcpus に対応し、current属性の値が -smp X の Xに相当します。

QMPでのCPU Hotplug

QMP(Qemu Monitor Protocol)でQemuと通信してCPU Hotplugを実施します。
QMPの使い方については下記のブログがとても詳しいです。
Multiple ways to access Qemu Monitor Protocol(QMP)
QMPで下記のコマンドを送信します。
> {"execute":"cpu-add", "arguments" : { "id" : 1 } }
arguments の id が Hotplug 対象の 仮想CPU です。この値は、0以上、maxcpus未満の整数をしていします。
あとはゲスト内でCPUをonlineにします。
# echo 1 >  /sys/devices/system/cpu/cpu1/online
/proc/cpuinfoなどで、Hotplugされたことを確認しましょう。

virsh での CPU Hotplug

libvirt が Qemu CPU Hotplug をサポートしているのは version 1.0.6.5 からですが、Fedora19 の libvirt 1.0.5.5 ではサポートされているので、それを使います。
関連コミット:
qemu: Implement new QMP command for cpu hotplug

注意: CPU Hotplugを使うには チップセットエミュレータのversionが1.5以上でないといけません。virsh edit で
<os>
  <type arch="x86_64" machine="pc-1.2">hvm</type>
</os>

<os>
  <type arch="x86_64" machine="pc-1.5">hvm</type>
</os>
に変更してください。
virsh setvcpus コマンドで仮想CPUをHotplugします。仮想マシンの名前は hotplug としています。
# virsh vcpucount hotplug
maximum      config         4
maximum      live           4
current      config         1
current      live           1
# virsh setvcpus hotplug
# virsh vcpucount hotplug
maximum      config         4
maximum      live           4
current      config         1
current      live           2
あとはQMPでの場合と同様に、ゲスト内でHotplugされたCPUをonlineにするだけです。

virt-manager での CPU Hotplug

注意: virsh での CPU Hotplug と同様に、チップセットエミュレータのversionが1.5以上であることを確認しましょう。同様に、 libvirt の version についても確認しましょう。
virt-manager での CPU Hotplugは実は簡単で、下記仮想マシンの詳細管理画面で、CPUの"現在の割り当て"部分をポチポチして"適用"ボタンを押すだけです。便利だなぁ。
後は、Hotplug されたCPUをゲスト内でonlineにしましょう。

Qemu guest agent との連携

Hotplug されたCPUをいちいちゲスト内で online にするの、めんどくさいですね。そんなときは Qemu guest agent と連携してホストから CPU を online にしましょう。ゲストにQemu 1.5 以降の guest agent をインストールして起動したあと、ホストから "guest-set-vcpus" コマンドで guest agent 経由で CPU を online にできます。

guest agent の設定の仕方については下記の記事が詳しいです。
lost and found(for me?) : Fedora 19 KVM : qemu-guest-agent

virsh で CPU を Hotplug したあと、guest agent 経由で online にします。
# virsh qemu-agent-command hotplug '{"execute":"guest-get-vcpus"}'
{"return":[{"online":true,"can-offline":false,"logical-id":0},{"online":true,"can-offline":true,"logical-id":1},{"online":true,"can-offline":true,"logical-id":2}]}
# virsh setvcpus hotplug 2
# virsh qemu-agent-command hotplug '{"execute":"guest-get-vcpus"}'
{"return":[{"online":true,"can-offline":false,"logical-id":0},{"online":false,"can-offline":true,"logical-id":1}]}
# virsh qemu-agent-command hotplug '{"execute":"guest-set-vcpus", "arguments" : { "vcpus" : [{"online":true,"can-offline":false,"logical-id":0},{"online":true,"can-offline":true,"logical-id":1}] }}'
{"return":2}

[root@edge2 qemu]# virsh qemu-agent-command hotplug '{"execute":"guest-get-vcpus"}'
{"return":[{"online":true,"can-offline":false,"logical-id":0},{"online":true,"can-offline":true,"logical-id":1}]}

/proc/cpuinfoでちゃんとonlineになってることを確認したらOKです。

参考文献

Qemu : qga/qapi-schema.json
Multiple ways to access Qemu Monitor Protocol(QMP)
lost and found(for me?) : Fedora 19 KVM : qemu-guest-agent

2013/07/21

VirtFS で Qemu ゲストホスト間ファイル共有

はじめに

Qemu/KVM 環境において、ホストゲスト間でのファイル共有ができると、とても便利です。例えば、開発中の Linux Kernel をテストする時には、ホストのコンパイル済み Kernel ソースディレクトリをゲストでマウントし、Kernel のインストールができると捗ります。ファイル共有方法には NFS、CIFS、SSHFS などがありますが、Qemu にはより効率的な "VirtFS" という仕組みがあります。
VirtFS は、ゲストの Linux マシンと virtio-9p デバイスを通じてファイル共有する仕組みです。ゲストホスト間で共有するリングバッファへの読み書きでデータをやり取りするため、他のネットワークファイルシステムなどより効率が良いのです。
今回は virt-manager での VirtFS を使ったファイル共有設定方法についてご紹介します。
Fedora 18で検証しています。(Fedora 15以上であれば Qemu が対応しています。)

virt-manager でのホストゲスト間ファイル共有設定

仮想マシン詳細を開き、「ハードウェアを追加」で 「FileSystem」を選択します。

ファイルシステム・パススルーの各項目を設定していきます。
上記の設定のように設定し、ゲストを起動しましょう。

「ターゲットパス」に指定したワードが、ゲスト上で 9pfs をマウントする際のマウントタグになります。

ゲストでのマウント
マウントタグの確認
# cat /sys/bus/virtio/drivers/9pnet_virtio/virtio<n>/mount_tag
source_tag
マウント
# mkdir source
# mount -t 9p -o trans=virtio source_tag ./source/
これでゲストから /path/to/source_dir がみえるようになりました。

注意

上記の設定では、ゲストからの共有ディレクトリへの書き込みが Permisson Denied になってしまいます。これは、qemu 起動ユーザが共有ファイルへのアクセス権を持っていない時に生じます。書き込みできるようにするためには、qemu を root で起動する必要があります。

qemu を root で起動するための libvirt 設定
/etc/libvirtd/qemu.conf
user = "root"
group = "root"
をコメントアウトし、libvirtd を再起動。

参考文献

Documentation/filesystems/9p.txt
libvirt: Domain XML format
Qemu Wiki : 9psetup
VirtFS LPC 2010
KVM日記 : Rootfs over Virtfsでゲストを起動する

2013/07/18

virsh で 仮想マシンのスナップショットを取る

はじめに

仮想マシン上で頻繁に環境構築・破壊を繰り返す場合、仮想マシンのスナップショットを利用し、素早くディスク状態をもとに戻せると便利です。libvirt, Qemu/KVM は仮想マシンのスナップショット機能を実装しており、とても有用です。今回は virsh コマンドでのスナップショットの扱い方をご紹介します。検証環境は Fedora 18です。

スナップショットの種類

libvirt, Qemu が実装している仮想マシンスナップショットの種類には、以下の2種類がありあます。

- 1. 内部スナップショット
- 2. 外部スナップショット

1. 内部スナップショットは仮想マシンのスナップショットを一つの qcow2 ファイルで管理する方式です。スナップショット取得中は仮想マシンは一時停止状態になります。仮想マシンのディスクのスナップショットのみならず、RAM 状態やデバイス状態などの仮想マシン状態も保存できます。

2. 外部スナップショットは仮想マシンのスナップショットを外部の qcow2 ファイルで管理します。なんと、仮想マシンを停止することなくスナップショットを取得できます。仮想マシンディスク以外の仮想マシン状態を保存することは、今のところできません。また、今のところ、仮想マシン停止中にはスナップショットを取ることができません。
現状動作が安定しておらず、非常に実験的な機能です。

以下、仮想マシンの名前を vm1 として、virsh コマンドの使い方を説明します。

内部スナップショット

内部スナップショットの作成
# virsh snapshot-create-as vm1 snap1 "snap1 description"
ドメインのスナップショット snap1 が作成されました
内部スナップショットは仮想マシン稼働中でもスナップショットを作成できます(ただし、安定していません)。作成している間は、仮想マシンは一時停止状態になります。ストレージ性能や仮想ディスク容量にもよりますが、作成時間は数分かかります。

内部スナップショット確認
# virsh snapshot-list vm1
 名前               作成時間              状態
------------------------------------------------------------
 snap1                2013-07-18 16:43:11 +0900 running

内部スナップショット復元
# virsh snapshot-revert vm1 snap1
スナップショットの復元についても、仮想マシン稼働中に実行可能です。ただし、復元中、仮想マシンは一時停止状態になります。

内部スナップショット情報の取得
指定のスナップショット情報を取得する際のコマンドは以下です。
# virsh snapshot-info vm1 snap1
名前:         snap1
ドメイン:   vm1
カレント:   はい (yes)
状態:         running
親:            -
子:            0
子孫:         0
メタデータ: はい (yes)

スナップショット復元後は下記コマンドで現時点でどのスナップショットを使用しているか確認できます。
# virsh snapshot-info vm1 --current
名前:         snap1
ドメイン:   vm1
カレント:   はい (yes)
状態:         running
親:            -
子:            0
子孫:         0
メタデータ: はい (yes)

スナップショット XML ファイルのダンプ
仮想マシンに関する設定情報(XML ファイル)を含んでいます。下記コマンドで設定情報を出力できます。
# virsh snapshot-dumpxml vm1 snap1

スナップショットの削除
# virsh snapshot-delete vm1 snap1


外部スナップショット

外部スナップショット作成
# virsh snapshot-create-as vm1 disksnap1 "disksnap1 description" --disk-only --atomic
ドメインのスナップショット disksnap1 が作成されました
外部スナップショットは仮想マシン実行中のみ取得可能です。内部スナップショットとは異なり、仮想マシンを停止(一時停止)することなく取得可能(Live Snapshot)です。つまり、仮想マシン無停止での Live Backup が可能です。
外部スナップショット作成後はディスクスナップショットイメージが作成され、current snapshot が作成したスナップショットになります。
# virsh snapshot-info vm1 --current
名前:         disksnap1
ドメイン:   vm1
カレント:   はい (yes)
状態:         disk-snapshot
親:            -
子:            0
子孫:         0
メタデータ: はい (yes)

外部スナップショット確認
# virsh snapshot-list vm1
 名前               作成時間              状態
------------------------------------------------------------
 disksnap1            2013-07-18 17:39:44 +0900 disk-snapshot
 snap1                2013-07-18 16:43:11 +0900 running

外部スナップショットが作成されると、仮想マシンイメージファイルを格納してあるディレクトリ(デフォルトでは /var/lib/libvirt/images)にスナップショットファイル(vm1.disksnap1)が新たに作成されます。
仮想マシンは新たに作成されたスナップショットファイルを使用するようになります。
# virsh domblklist vm1
ターゲット ソース
------------------------------------------------
vda        /home/eiichi/vmimg/vm1.disksnap1
hdc        -

外部スナップショット復元
外部スナップショットの復元は、virsh edit で仮想マシン設定 XML ファイルを開き、disk タグの source タグのfile 属性を復元したいディスクスナップショットに指定します。現状では ディスクスナップショットへの snapshot-revert は対応していないようです。
# virsh snapshot-revert vm1 disksnap2

エラー: サポートされない設定: 外部ディスクスナップショットへの復元はまだサポートされていません

注意
外部スナップショットはまだまだ開発段階の機能です。無停止でスナップショットが取れますが、動作が安定しないのが難点です。また、内部スナップショット機能についても、仮想マシン起動中のスナップショット取得はやはり安定して動作しないことがあります。
安定した動作を希望する場合、一番安全な、"仮想マシン停止時"  の "内部スナップショット" をおすすめします。

参考文献
fedoraproject : Features/Virt Live Snapshots
QEMU wiki : Features/Snapshot
libvirt : Snapshot XML Format
kashyapc fedorapeople : snapshot handout

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" を指定する必要があります。
# ./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/09

Qemu ゲストからの virtio-blk cache 設定確認

Qemu/KVM を用いた VPS(IaaS) を使っていると、Qemu virtio-blk Disk の cache  設定がどうなっているのか気になることがあります。実は、ゲストから以下のコマンドで cache 設定を確認できます。
(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 を差し込む箇所を確認します。
[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 を有効にします。
$ ./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 が必要です。

2013/02/25

qemu cache mode まとめ

はじめに

qemu の cache mode には "none", "direcsync", "writeback", "writethrough", "unsafe" の5つがあります。名前からでは、cache mode のそれぞれの仕組みがよくわかりません。そのために、どのようなケースでどの cache mode を使えばいいのか、判断に困ってしまいます。
ここでは、raw ディスクイメージについて、それぞれの cache mode の違いをまとめます。(qcow2 など他のディスクイメージフォーマットでは仕組みが若干ことなります)

Cache Mode 性質比較
それぞれの cache mode ではHost page cache や仮想的な Disk write cache を用いるかどうかで性質が変わってきます。表でまとめると以下のようになります。


cache mode Host page cache Disk Write cache NO Flush
directsync



writethrough


none


writeback

unsafe
✓ : on

Host page cache とは、qemu が 仮想マシンイメージファイルを open するときに O_DIRECT フラグをつけるか否かです。"none" と"directsync" では O_DIRECT フラグを"つける" ので、Host page cache を使いません。

Disk Write Cache とは、virtio-blk デバイスの持つ揮発性 cache です。現在では通常のHDDなどは32MB や 64MB のcache を持っていますが、Disk write cache は virtio-blk が持つ仮想的な cache です。qemu の中では Disk Write Cache を使うか否かで flush(fdatasync) のタイミングが異なります。使う場合はOSから flush 要求があった時に flush(fdatasync) しますが、使わない場合は disk write の度に flush(fdatasync)します。qemu が fdatasync を発行することで、Host の Disk にデータが書きだされますが、頻繁に fdatasync すると性能が劣化してしまします。

NO Flush とはqemuが保持している cache を disk へ flush するのを無効化します。無効化することで性能は上がりますが、Host クラッシュ時にゲストでファイル不整合が起こる可能性が増します。

以上を踏まえると、cache mode は以下のように分類できます。
こちらのほうがわかりやすいと思います。

Host page cache あり
Host page cache なし
Write 毎 にFlush
writethrough
directsync
Flush 要求時に Flush
writeback
none
Flush 無効
unsafe


性能比較
それぞれのcache mode のライト性能を、dd で測定しました。
環境は以下となっています。

  • HostOS: Fedora 18 (3.7.9-201.fc18.x86_64)
  • GuestOS : 同上
  • Host FS : ext4 (ordered)
  • Guest FS : 同上
  • Host memory : 4GB
  • Guest memory : 1GB
  • qemu : 1.4
  • qemu aio : threads
  • qemu disk type : raw

測定コマンドは
$ dd if=/dev/zero of=./zero bs=64K count=8K 
もしくは
$ dd if=/dev/zero of=./zero bs=64K count=8K oflag=direct
です。
Cache Mode の性質を比較するために、direct flag をつけた場合でも測定しています。

比較表

通常 write(MB/s)
oflag=direct(MB/s)
HOST
96.5
82.3
directsync
65.8
4.8
writethrough
62.8
4.6
none
88.3
54.2
writeback
80 ~ 400
80 ~ 400
unsafe
80 ~ 400
80 ~ 400

"writeback" と "unsafe" ではブレが非常に大きくなっています。これは Host の Page cache の影響です。 "directsync" と "writethrough" では、oflag=direct 時に著しく性能が落ちています。これは qemu が write 毎に fdatasync を発行するためです。

Cache mode の選定
さて、cache mode には色々あり、性質も大きく異なりますが、結局どの mode を選べば良いのでしょうか。指標としては、"メモリ消費量"、"性能"、"安全性" が挙げられます。

"メモリ消費量" で mode に大小をつけると以下のようになります。
directsync = none < writethrough = write back = unsafe
"directsync" と "none" では Host page cache を使用しないため少なくなります。他の mode では Host page cache が効くため、Guest と Host で二重にキャッシュが効いていることになります。つまり、同じデータがメモリ上に重複して存在してしまします。KSM(Kernel Samepage Merging)の仕組みがあるとは言え、キャッシュ重複はメモリを消費するため、好ましいものではありません。

"性能" での優劣は以下となります。
directsync < writethrough < none < writeback < unsafe
"writeback" と "unsafe" は広大な Host page cache を効かせる分、性能は良くなります。write 毎に fdatasync を発行する "directsync" や "writethrough" は性能は劣ります。

"安全性" での優劣は以下となります。ここで、安全性とは、ファイル整合性がとれるまでの時間が短いものを優とします。
unsafe < writeback < none < writethrough = directsync
fdatasync を頻繁に発行する "writethrough" と "directsync" はファイル整合性を取りやすいです。cache を効かせ、Flush 頻度が少ないものほど、安全性は劣ります。

以上を踏まえて、cache mode を選定します。
要件により、選定する cache mode は異なります。

1. とにかく安全第一。物理環境と同程度の安全性が欲しい。
⇛ directsync
cache mode で一番安全なのは O_DIRECT で fdatasync を頻発する directsync です。そのかわり、性能はいまいちです。

2. メモリ消費量は程程に、性能もある程度欲しい。
⇛ none
"none" は Host page cache を使わないため、メモリ消費もそれほど大きくありません。性能も Host には劣りますが、まずまずです。

3. メモリをどれだけ消費しようが、性能が欲しい。
⇛ writeback
"writeback" は広大な Host page cache を使うため、性能が出ます。反面、メモリ消費量は大きいです。

普通に VM をいじる分には "writeback" で。メモリ が多くない環境では "none" にするのがいいと思います。
ちなみに、自分はいつも "none" にしています。

参考文献
An Updated Overview of the QEMU Storage Stack
QEMU Emulator User Documentation
[Qemu-devel] Is cache=writeback safe yet

2013/02/18

qemu virtio-blk data plane の使い方

はじめに
qemu virtio-blk data planeとは、virtio-blk デバイスごとに、専用のIO threadを作り、QBL(qemu big lock)の外部で動作させることでlock contentionによる性能劣化を抑える試みです。専用のthreadを作り性能を向上させる試みは、ネットワークではすでにvhost-netを用いて行われていました。

関連コミット:
http://git.qemu.org/?p=qemu.git;a=commit;h=392808b49b6aee066d0c1d200e72fc3dc11c9d0f

簡単に仕組みを説明すると、virtio-blk デバイスごとに作られたIO threadはLinux Native AIOシステムコールを使い、非同期にIOを発行します。ゲストからの通知(ioeventfd)、およびゲストへの割り込み通知(irqfd)もこのIO threadが担当します。従来はqemuが行なっていた作業をglobal mutex外へと分離し、専用のthreadに任せることでScalabilityと性能を向上させることができるわけです。

virtio-blk data planeを用いることで、IOPSが14万から60万に上がったという報告もあります。
http://comments.gmane.org/gmane.comp.emulators.qemu/184821

しかし、現状ではvirtio-blk data plane には以下のような制限があります。
  • raw format のみのサポート
  • live migration 非サポート
  • hot unplug 非サポート
  • qemu での IO throttling が効かない
  • Host は linux のみサポート(Linux Native AIO syscallを使うため)

使い方
virtio-blk x-data-plane を使うには、まず条件として以下が必要です。

  • raw image formatでなければならない
  • wce(write cache enable: Guestのwrite cache設定を起動中に on/off 変更する機能) off
  • scsi off

「raw image なんてないよ、qcow2(or 3) しかないよ!」という方は以下のコマンドでraw image を作ってVMを新規作成しまししょう。
# qemu-img convert -O raw qcow.img raw.img
qemu を直接起動しても良いのですが、ここでは利便性のためにlibvirtを経由してqemuを扱うことにします。
上記の条件に合うように、libvirt XML file を作りましょう。
例えば、以下のようにします。

<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
...
  <devices>
    <emulator>/usr/local/bin/qemu-system-x86_64</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='raw' cache='none' io='native'/>
      <source file='/home/eiichi/vmimg/fraw.img'/>
      <target dev='vda' bus='virtio'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
    </disk>
    ...
  </devices>
  <qemu:commandline>
    <qemu:arg value='-set'/>
    <qemu:arg value='device.virtio-disk0.config-wce=off'/>
    <qemu:arg value='-set'/>
    <qemu:arg value='device.virtio-disk0.scsi=off'/>
    <qemu:arg value='-set'/>
    <qemu:arg value='device.virtio-disk0.x-data-plane=on'/>
  </qemu:commandline>
</domain>

diskタイプをraw、AIOはLinux Nativeを指定します。
また、qemuの-setオプションを使うことで、x-data-plane=on オプションを有効にします。

評価
以下の環境で、bonnie++による書き込み性能評価を行いました。
  • Host OS: Fedora 18
  • Host Memory : 4G
  • Host FileSystem : btrfs
  • Guest OS: Fedora 18
  • Gues VCPU: 2
  • Guest Memory : 1G
  • Guest FileSystem : btrfs
  • Qemu : qemu v1.3.0-rc2
  • 測定コマンド : bonnie++
Guest data plane off:
Version  1.96       ------Sequential Output------ --Sequential Input- --Random-
Concurrency   1     -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
fraw             2G   875  84 74157  13 21666   6  3128  78 50335   6 263.5  15
Latency             31893us     632ms    6507ms   83291us     299ms     728ms
Version  1.96       ------Sequential Create------ --------Random Create--------
fraw                -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                 16 21634  57 +++++ +++ +++++ +++ 22124  60 +++++ +++ +++++ +++
Latency               241us     593us     443us     108us      30us     138us

Guest data plane on:
Version  1.96       ------Sequential Output------ --Sequential Input- --Random-
Concurrency   1     -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
fraw             2G   900  87 61252  12 24206   7  3296  82 91454  12 242.6  15
Latency             54769us    1189ms    5273ms   36272us     275ms     546ms
Version  1.96       ------Sequential Create------ --------Random Create--------
fraw                -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                 16  6949  18 +++++ +++ +++++ +++ 19625  52 +++++ +++ 32179  21
Latency              1069us    1450us     448us     112us     449us     173us

残念ながら、自分の環境だと性能に大きな違いは見受けられないようです。。
高性能なストレージを使ったり、qemu_global_mutex contention が大量に発生する環境で測定すると、また違った結果がでてくると思われます。

参考文献

An Updated Overview of the QEMU Storage Stack
Optimizing the QEMU Storage Stack
[Qemu-devel] [PATCH v2 0/8] virtio: virtio-blk data plane
Features/WriteCacheEnable


2013/02/02

手軽にqemuのトレースを採る

qemu内部のロジックを追ったり、性能解析をするにはトレース採取が有効です。qemuのトレースを手軽に採る方法についての投稿です。

qemuのトレースを採る方法については、いくつかありますが、標準エラー出力にトレースを書き出す "stderr" trace-backend がもっとも簡単な方法です。
"stderr" backend の仕組みは qemu 内部のトレースイベントを標準エラー出力にデバッグprintfをするという非常に単純なものです。
qemu-kvm 0.15よりサポートされています。

関連コミット:
http://git.qemu.org/?p=qemu.git;a=commit;h=320fba2a1f384e17db150d74540a2cf005eb47b5


使い方
qemuのトレースは採取しない設定でもオーバヘッドがあるため、デフォルトではオフになっています。トレースを有効にするには、コンパイル時にトレースを有効にする必要があります。
コンパイルオプションは以下のようになります。
./configure --prefix=/usr/local/ --enable-trace-backend=stderr

トレースイベント一覧は以下のファイルにかかれています。
qemu/trace-events

イベント一覧から、取得したいイベントを選びファイルに書き出します。 例えば、以下のような内容のファイルを作ります。ワイルドカード指定が可能です。
g_malloc
g_free
qemu*
virtio*

qemu を起動する際に、このイベントファイルを-traceオプションに指定します。
sudo ./qemu-system-x86_64 -enable-kvm -hda ~/vmimg/f1.img -m 512 -trace events=/path/to/events-file

得られる出力は以下のようになります。
g_malloc size 16 ptr 0x7f3f40004270
qemu_co_mutex_lock_entry mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_lock_return mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_unlock_entry mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_unlock_return mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_coroutine_yield from 0x7f3f54a7faa0 to 0x7f3f400009d8
qemu_coroutine_enter from 0x7f3f54a7fbb8 to 0x7f3f54a7faa0 opaque (nil)
qemu_co_mutex_lock_entry mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_lock_return mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_unlock_entry mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_unlock_return mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
g_free ptr 0x7f3f40004270


libvirtとの連携
qemuを利用する際は、通常libvirt経由で利用することが多いと思います。ここではlibvirtから"stderr" trace-backend を利用する際のtipsを記載します。

・libvirtのログに出力
virsh start domain コマンドでドメインを起動した場合、stderrの出力先は、以下になります。
/var/log/libvirt/qemu/domain.log

・動的なイベントのオン・オフ
qemu monitor command を利用することで、稼働中のドメインで採取するイベントを動的にon/offできます。

利用可能なトレースイベント一覧の取得
# virsh qemu-monitor-command --hmp domain info trace-events

イベントのon・off
# virsh qemu-monitor-command --hmp domain trace-event qemu_vmalloc on
# virsh qemu-monitor-command --hmp domain trace-event qemu_vmalloc off
参考情報

Observability using QEMU tracing
qemuのtrace機能
docs/tracing