VT-xとかAMD-Vとかって,なんでいるん?

VMX(VT-xとかAMD-Vとか)ってなんで必要なのか,わからなかったので,調べてた.

仮想マシンモニタは,効率的に処理したいので,仮想プロセッサで処理する内容を実プロセッサでそのまま処理したい.そうしたときに,単に加算するとかならいいとしても,レジスタとか触るとかいくつかの命令には問題があるらしい.

プロセッサのPSW触るとかは,OSがトラップして特権で動かすのでOK.

センシティブ命令ってのがヤバイらしい.例えばレジスタ触るPOP, PUSHとかの命令らしくて,CPUの状態を触るのに特権じゃなくてOKなので困る.どうヤバイかというと,仮想プロセッサなんて実はないので,あるかのようにゲストOSがCPUに命令を発行しても実行できない.しかもセンシティブ命令は特権じゃなくてOKで,ユーザモードで動くので,仮想マシンモニタがトラップできない.(仮想マシンモニタ的には,加算命令とかと区別がつかない)
なのでこれを解決しようとすると,(1)センシティブ命令を事前に書き換えるか,(2)実行時に書き換えるか,(3)センシティブ命令を特権命令にする(仮想マシンモニタでトラップしてエミュレートする)かの3択で,あとはよくある話.

結局のところ,仮想プロセッサなんて無いのに,ゲストOSがマジで存在しているかのように使ってくるので(当然のことだけれど),整合性が取れるようにしなきゃいけないので出来るようにしたって話.

と思った.


見てたページ:
Kernel/VM Advent Calendar 38日目: 仮想化可能なアーキテクチャの要件とx86 - yuyarinの日記
第4回 x86プロセッサにおけるプロセッサ仮想化:エンジニアなら知っておきたい仮想マシンのしくみ|gihyo.jp … 技術評論社
第5回 x86プロセッサの仮想化支援機能Intel VT & AMD-V:エンジニアなら知っておきたい仮想マシンのしくみ|gihyo.jp … 技術評論社

env EDITOR=vipw vipwしたらエラーになる件について

面白いこと聞いたので調べてみた.

# env EDITOR=vipw vipw
usage: vipw [-d directory]
vipw: pw_edit(): No such file or directory

この2行のエラーについて.環境はFreeBSD 9.0-RELEASE.


vipwのソースコードは/usr/src/usr.sbin/vipw

# wc vipw.c
     137     566    3804 vipw.c

なんとお手軽……(ゴクリ

vipw の概要は,初期化 → pw_init() → pw_lock() → pw_tmp() → pw_edit() -> pw_mkdb() → pw_fini() な感じ.vipw.c では EDITOR 環境変数を読まない.pw_hoge() は /usr/src/lib/libutil/pw_util.cにあるのでそっちをみる.


見てみると,EDITOR環境変数を読み込んで,無ければviを設定してる.

/usr/src/lib/libutil/pw_util.c
  289 pw_edit(int notsetuid)
  290 {
...
  297     if ((editor = getenv("EDITOR")) == NULL)
  298         editor = _PATH_VI;


その次はパスワードファイルを編集するプロセスを fork する.321 行目でエディタを起動.env EDITOR=vipw してるので,vipwに tempname を引数として与えて起動することになる.man vipw すれば分かる通り,vipw に -d オプション以外の引数はエラーになる.
これが1行目のエラー.
このときに errno が -1 になって,_exit() で子プロセス(env EDITORの方)の戻り値は -1 に.

/usr/src/lib/libutil/pw_util.c
  289 pw_edit(int notsetuid)
  290 {
...
  309     switch ((editpid = fork())) {
  310     case -1:
  311         return (-1);
  312     case 0:
  313         sigaction(SIGINT, &sa_int, NULL);
  314         sigaction(SIGQUIT, &sa_quit, NULL);
  315         sigprocmask(SIG_SETMASK, &oldsigset, NULL);
  316         if (notsetuid) {
  317             (void)setgid(getgid());
  318             (void)setuid(getuid());
  319         }
  320         errno = 0;
  321         execlp(editor, basename(editor), tempname, (char *)NULL);
  322         _exit(errno);
  323     default:
  324         /* parent */
  325         break;
  326     }


vipw に戻って,switch(pw_edit(0)) してるところを見ると,子プロセス(env EDITORの方)の戻り値が -1 だったので, err(1, "pw_edit()") になる.
これが2行目のエラー.

/usr/src/usr.sbin/vipw/vipw.c
  103     for (;;) {
  104         switch (pw_edit(0)) {
  105         case -1:
  106             pw_fini();
  107             err(1, "pw_edit()");
  108         case 0:
  109             pw_fini();
  110             errx(0, "no changes made");
  111         default:
  112             break;
  113         }
  114         if (pw_mkdb(NULL) == 0) {
  115             pw_fini();
  116             errx(0, "password list updated");
  117         }
  118         printf("re-edit the password file? ");
  119         fflush(stdout);
  120         if ((line = fgetln(stdin, &len)) == NULL) {
  121             pw_fini();
  122             err(1, "fgetln()");
  123         }
  124         if (len > 0 && (*line == 'N' || *line == 'n'))
  125             break;
  126     }


tempname は masterpasswd から生成してて,masterpasswd と同じディレクトリに,ファイル名を pw.XXXXXX にして一時ファイルを作成する.

/usr/src/usr.sbin/vipw/vipw.c
  213 int
  214 pw_tmp(int mfd)
  215 {
...
  221     if (*masterpasswd == '\0')
  222         return (-1);
  223     if ((p = strrchr(masterpasswd, '/')))
  224         ++p;
  225     else
  226         p = masterpasswd;
  227     if (snprintf(tempname, sizeof(tempname), "%.*spw.XXXXXX",
  228         (int)(p - masterpasswd), masterpasswd) >= (int)sizeof(tempname)) {
  229         errno = ENAMETOOLONG;
  230         return (-1);
  231     }


masterpasswd は _PATH_MASTERPASSWD から生成してる._PATH_MASTERPASSWD は/usr/include/pwd.h に書いてある.

   93 int
   94 pw_init(const char *dir, const char *master)
   95 {
...
  110     if (master == NULL) {
  111         if (dir == NULL) {
  112             strcpy(masterpasswd, _PATH_MASTERPASSWD);
  113         } else if (snprintf(masterpasswd, sizeof(masterpasswd), "%s/%s",
  114             passwd_dir, _MASTERPASSWD) > (int)sizeof(masterpasswd)) {
  115             errno = ENAMETOOLONG;
  116             return (-1);
  117         }
  118     } else {
  119         if (strlen(master) >= sizeof(masterpasswd)) {
  120             errno = ENAMETOOLONG;
  121             return (-1);
  122         }
  123         strcpy(masterpasswd, master);
  124     }       
/usr/include/pwd.h
   64 #define _PATH_PWD       "/etc"
   65 #define _PATH_PASSWD        "/etc/passwd"
   66 #define _PASSWD         "passwd"
   67 #define _PATH_MASTERPASSWD  "/etc/master.passwd"
   68 #define _MASTERPASSWD       "master.passwd"

ip_fastforward

FreeBSD5.3からIP Fastforward(IPv4)という機能があるらしく,これを有効にすると早く処理できるらしい.
http://people.freebsd.org/~andre/FreeBSD-5.3-Networking.pdf

# sysctl net.inet.ip.fastforwarding=1

ソースはsys/netinet/ip_fastforward.cで,コメントが非常に丁寧なので中身見ればOK.あとはしゅううさんの記事見ればOK.http://d.hatena.ne.jp/syuu1228/20081020

やっていることは,パケットの正常性確認,自宛処理,NAT/FW,ネクストホップ探索,TTL減算・チェックサム更新とか.
この内,NAT/FWはpfil_run_hooks()に,ネクストホップ探索はip_findroute()に丸投げ.
あと,自宛確認をin_localip()でやってたりもする.

Nested virtualization on Mac (using VMware Fusion and ESXi)

Macでの多段仮想化(VM on VM/Nested Virtualization)について.
Macでの」というのはVMware Fusionを使うということです.Type2 VMMでVT-x/EPTを透過できるので有名なVMware Fusionを使いました.VirtualBoxだとVt-x/EPTを透過できないみたいなことを聞いたことがあるので,そちらではやっていません.

最終的には Gentoo/Linux over ESXi over VMware Fusion over Mac OS X になりました.Gentoo/LinuxKVMを動かせば3段階のNestedになります.

下から順に設定を記載します.

VMware Fusion

ゲストOSへVT-x/EPTを透過させるには設定ファイルの書き換えが必要です.
ESXiの設定ファイル(例:~/Documents/Virtual Machines.localized/VMware ESXi 5.vmwarevm/VMware ESXi 5.vmx)に以下を追記しました.この項目は無いので新規追加です.

vhv.enable = "TRUE"

Fusionの各ゲストOS設定のOther→AdvancedにVT-x/EPTを有効にするかどうかの項目がありますが,これはデフォルトで自動設定になっているので設定不要でした.

ESXi

こちらもゲストOSへVT-x/EPTを透過させるには設定ファイルの変更が書き換えです.まずは設定ファイルを変更できるようにESXiへSSHでログイン出来るように設定を変更しました.ESXi(vSphere経由ではなく直接)のトラブルシュートの項目からESXiからSSHログインとシェルの有効化を行いました.ちなみに,この設定はvSphere経由でも出来るようです.
ログイン出来るようになったら/etc/vmware/configに以下を追記しました.この項目は無いので新規追加です.

vhv.allow = "TRUE"

実は設定はこれだけでは終わらず,ESXiのゲストOS個別設定が少し必要です.

一番上のOS(今回だとGentoo/Linux

ESXi上にインストールするのでWindows+vSphere Clientが必要です.今回はWindowsVMware Fusion上にインストールしました.全部VMware Fusion上にあることになります.
一番上のOSはなんでもよくて,今回はGentoo/LinuxのインストールCDを使いました.
ESXiがVT-x/EPTを透過させるにはゲストOSにも設定が必要です.この設定はvSphere Clientからは行えず,ESXiにSSHログインしてエディタで設定ファイルを直接編集する必要があります.
そのため,一旦ESXi上で動かすLinuxを作ったのちに,SSHログインしてVMの設定ファイルを修正します(例:/vmfs/volumes/datastore1/[仮想マシン名]/[仮想マシン名].vmx).上述のvhv.allow等とは異なり,項目はありますが右辺が違うのでそこを修正します.

guestOS = "vmkernel"
または
guestOS = "vmkernel5"

LinuxだからといってLinux2.6(64bit)等を選択するとVT-x/EPTが透過されません.vmkernel (ESXi4.0) / vmkernel5 (ESXi5.x) に設定する必要があるようです.
(これを入れると,仮想マシンのプロパティ→オプション→ゲストOS→バージョン:→VMware ESXi 4.0/5.xが選べるようになるので,以降はvSphereから設定することも可能です)

あとは grep vmx /proc/cpuinfo とかすれば vmx が有効になっていることがわかります.

きになったコト

今回使ったMacの物理CPUは1socket/4core/8threadで,この内4 processor coreをESXiに割り当てました.するとESXiからは4ソケットのCPUとして見えるようです.ESXi StandardでサポートされるCPU数上限が気になったのですが8 vCPUまでサポートしているようで,この制限に引っかかる事は無さそうです.また,メモリ上限は32GBで,同じくこちらも制限に引っかかることは無さそうです.

NPUのお仕事(Intel IXP 1200の場合)

2001年ぐらいにリリースされたNPU,Intel IXP1200の場合.

資料はこのへん.

  1. http://www.tu-ilmenau.de/fileadmin/public/iks/files/lehre/wi/WI-IXP.pdf
  2. http://www.cs.cmu.edu/~rajesh/papers/15740f00-ixp1200.pdf
  3. http://web.ict.kth.se/~mahidell/pubs/networking04_tutorial_final.pdf

NPUの制御はイーサネットコントローラ上部.
1番目の資料に全て書いてあるので,それをメモする.
NPUの構成については,マッチングを行うMicroEngine(ME),SRAM/DRAMおよびそれらのUnit,MAC層と接続しているIX Bus Unit,StrongARM(CPU)がある.

  1. イーサネットコントローラまで上がってきたフレームはRxFIFO(IX Bus Unitの一部)に格納される.
  2. MEが定期的にRxFIFOを監視していて(6MEx4Thread=24threadのうち2threadが監視用っぽい),フレームを見つけるまで監視し続ける
  3. RxFIFOにフレームを発見したMEは,フレーム全体をSDRAMにコピーするようにIX Bus Unitに指示する.そうすると後はIX Bus Unitが勝手にRxFIFOからSDRAMにコピーしてくれる.並行してMEは転送先を探索すべく,RxFIFOからフレームヘッダだけを取得してME内部のTransport Registerにコピーする.MEはこれとSRAMにあるテーブルとマッチングを行う.
  4. MEは必要に応じてSDRAM上のフレームのヘッダ情報を書き換える.
  5. 転送先の探索結果に基づいて転送する準備をする.MEがSDRAM UnitにIX Bus UnitのTxFIFOへコピーするように指示する.あとは勝手にSDRAM Unitがやってくれる.
  6. コピーが終わったらSDRAM UnitがMEに通知するので,それを持ってMEがIX Bus UnitへMAC層へ渡すように指示する.

マッチングに引っかからなかった時はStrongARMにあげて処理を決める.その結果(転送先情報)はStrongARMがSRAMに書き込む.

Network Algorighmics - まえがき

ネットワークには2種類のボトルネックがある

  • リソース ... スケールアウト,スケールアップ
  • 実装 ... 本書の対象

エンドノードのボトルネック

転送ノードのボトルネック


各章の概要

  • 第一部 network algorithmicsの方法の概要
    • 1章 Network Algorithmicsとは
    • 2章 Network Algorithmicsを考える前提条件・モデル
    • 3章 Network Algorithmicsの15の実装の基本的な考え方
    • 4章 Network Algorithmicsの15の実装詳細
  • 第二部 エンドノードに適用する場合
    • 5章 ウェブサーバの高速化
    • 6章 通信通信制御のオーバーヘッド削減(カーネルドライバ-アプリケーション間)
    • 7章 タイマ管理
    • 8章 ホストでの逆多重化
    • 9章:機能実装(バッファ割り当て,チェックサム等々)
  • 第三部 ルータ,スイッチに適用する場合
    • 10章 exact-match
    • 11章 prefix-match
    • 12章 packet classification(security+QoS)
    • 13章 スイッチング
    • 14章 パケットスケジューリング
    • 15章 ルータ
  • 第四部 network algorithmicsをセキュリティ・計測に使う場合
    • 16章 計測機能の実装方式
    • 17章 セキュリティ機能の実装方式
    • 18章 実例