VirtualBox 5.0の準仮想化クロックが遅れるときの対処法

Pocket

準仮想化クロックの遅れの原因を追究してみます

前回、準仮想化クロックが5分で6秒くらい遅れることがわかりました。

少し細かな話になりますが、ソースコードを探索して原因の追究ととりあえずの対処法を紹介します。

VirtualBoxのソースコードを見てみる

まずはVirtualBoxのソースコードをダウンロードします。以前いじったときから5年ほど時間がたっていました。

Googleの検索でparavirtualizationを検索しているとき、VirtualBox 5.0 Beta2 releasedのリリースノートに

VMM: added support for using Paravirtualization providers with raw-mode VMs

とありました。

raw-mode VMsはなんだろうということで、svn logでraw-modeを検索してみます。

すると、

r55068 disble GIM for raw-mode VMs

とあって、GIMは何だろうということになりました。そこでソースコードを検索してみると、

vbox/src/VBox/VMM/VMMR3/GIM.cppに説明がありました。

GIM providerはparavirtualization interfaceとして参照される。

準仮想化インターフェースはソースコードではGIMと呼ばれているんですね。GIMとは、Guest Interface Manager Deviceの略で準仮想化インターフェースのすべてを扱っています。

ソースコードには次のような説明があります。

準仮想化の1つのゴールはゲストをもっと正確に、もっと効率的にすることです。たとえばゲストOSは、ホストプロセッサの正しいTSCの周波数がGIM providerから供給されることに依存します。これによりゲストはTSC自身をキャリブレートすることを避けることができ、正確さと効率がよくなります。

ということは、VirtualBoxがTSCの周波数を供給していることになります。

また、ゲストOSの設定ファイルのところにあるVBox.logというログを眺めて、TSCを探します。すると、TSCモードをVirtTscEmulatedからRealTSCOffsetに変えると出ています。このRealTSCOffsetがあやしいです。

これは、vbox/src/VBox/VMM/VMMR3/TM.cppにあります。

このファイルを見てみると、u64CpuHzがCPUの周波数を設定しているようです。また、tmR3CalibrateTSC()という関数がTSCのキャリブレーションをしていそうです。

また、kvm-clockはメモリーにTSCと、それをシステム時間に変換するスケールファクターを保存していたことを頼りに、どこで設定しているかを探します。

すると、vbox/src/VBox/VMM/include/GIMKvmInternal.hにstruct GIMKVMSYSTEMTIMEという構造体のu32TscScaleがTSCをシステム時間に変換するスケールファクターであることがわかります。今度は、u32TscScaleを設定しているところをソースコードで探します。

u32TscScaleは、vbox/src/VBox/VMM/VMMR3/GIMKvm.cppでpVM->cTscTicksPerSecondを代入しています。

また、いろいろな情報をログファイルに出力していることに気づき、VirtualBoxのゲストの設定ファイルのところにあるVBox.logをGIMやTSCを目安に眺めてみるとcTscTicksPerSecondも数値が記録されていることがわかります。

このように、関連するであろうファイル、変数やマクロ名をソースコードをから探し出しました。

完全に理解するところまでは行かなかったのですが、重要と思われる変数などは抜き出せたように思います。

VMware Playerで気づいたこと

VirtualBoxと平行して使っていたVMware Playerでdmesg |grep tscとdmesg |grep TSCをやると次のように出ます。

kazu@Mars:~$ dmesg |grep tsc
[ 0.000000] tsc: Detected 3400.109 MHz processor
[ 1.595236] Switched to clocksource tsc
kazu@Mars:~$ dmesg |grep TSC
[ 0.000000] TSC freq read from hypervisor : 3400.109 MHz

CPUの周波数3400MHzとでて、TSCの周波数はhypervisorから供給されていると出ています。これは、今使っているCPUの定格の周波数です。

じゃあVirtualBoxの場合はどうなんだとなりました。ログファイルでcTscTicksPerSecondを見てみると、

00:00:01.277674 TM: cTSCTicksPerSecond=3 478 274 491 (0xcf5241bb) enmTSCMode=1 (VirtTscEmulated)

3478274491Hzということみたいです。3478MHzということで、VMware Playerより大きいです。

また、この違いが1分でどれくらいの差になるのか計算すると、

60 * 3400109000 / 3478274491 = 58.65 (秒)

となりました。ほぼ、5分で6から7秒の遅れということで、前回の計測とあっています。

ここまでわかっていること

  • ゲストOSのクロックソースがkvm-clockとtscの場合時計が遅れる。遅れるのは5分で6秒ほど。
  • 準仮想化クロックでは、TSCの周波数はハイパバイザーがゲストOSに伝える。ゲストOSではキャリブレートしない。
  • VMware Playerとの比較では、cTSCTicksPerSecondの値が大きい。その値は、時計の遅れと一致している。

ということがわかりました。

とりあえずの対処法

VirtualBoxのフォーラムをGoogleでcTSCTicksPerSecondを検索すると、VBoxManageでゲストOSのTSCTicksPerSecondを設定する項目があるという情報を見つけました。

[solved] detected frequency

この項目が、準仮想化にも関係するのかよくわかりませんでしたが、次のように設定してから立ち上げました。

VBoxManage setextradata "[VMName]" "VBoxInternal/TM/TSCTicksPerSecond" 3400109000

周波数の値は、VMware Playerの値を使っています。わからなければ、CPUの定格の周波数でいいと思います。すると、時計の遅れはなくなりました。

これは、とりあえずの対処になります。TSCの周波数を決め打ちしています。TSCの周波数がCPUの電源管理状態によらず一定でないとできない対処です。最近のCore iシリーズ以降であれば使えると思います。でもこれで、VirtualBoxのTSCの周波数を決めるところにバグがあることがわかりました。

原因がつかめたようでよかったです。早くバグが修正されるといいなと思います。

Pocket

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください