Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

BitVisorの外部割り込み処理

More than 1 year has passed since last update.

BitVisorの外部割り込み処理について簡単に説明します.アーキテクチャはIntelが対象です.

基本

ゲスト実行中に割り込みでVMEXITが発生するかどうかは設定によります.VTではVM-Execution Control FieldsのPin-Based VM-Execution Controls内のexternal-interrupt exitingが1だと外部割り込み時にVMEXITします (exit reason は External Interrupt).そうでなければ,割り込みはそのままゲストに通知されることになり,RFLAGS.IF=1ならゲストの割り込みハンドラが処理します.BitmapでVMEXITする番号が指定できるMSRとは異なり,特定の割り込み番号だけVMEXITさせることはできません1

外部割り込みでVMEXITしたときの割り込みの扱いは以下のようになります.

  • VM-Exit controlsのAcknowledge interrupt on exitが1のとき
    • HWが割り込みコントローラにackしてくれます.割り込み番号はVM-exit interruption-information fieldに格納されます.
  • そうでないとき
    • 割り込みはRFLAGS.IFでマスクされた状態です.この状態でホストが割り込みを有効化すると割り込みが入り,ホストの割り込みハンドラが起動します.

一般に外部割り込みはVMに向けたものとは限らないので,ホスト側の割り込みハンドラで処理するためにacknowledge interrupt on exitは0にすることが多いようです.

BitVisorの処理

まず,BitVisorは割り込み禁止状態で動作しています(なお,VMEXITするとRFLAGS.IFはクリアされます).またBitVisorではデフォルトでexternal-interrupt exitingが1, acknowlege interrupt on exitが0です.defconfigでno_intr_intercept = 1にするとexternal-interrupt exitingが0になります.

External InterruptでVMEXITすると,do_external_int ()を実行しますがここではvt_exint_assert (true)を呼んで外部割り込みがあったとマークするだけです.実際の対応はメインループからvt_interrupt() => vt_inject_interrupt()で実行します.ここで num = current->exint.ack ()によって一時的に割り込みを有効化して割り込みを受け取り,割り込み番号を取得します.そしてvt_generate_external_int (num)で割り込みをゲストに再挿入します.どうせ再挿入するならexternal-interrupt exitingする意味がないように見えますが,いくつかのケースでexternal-interrupt exitingが有用な場合があります.

一つは,定期的にVMEXITを起こしたい場合です.BitVisorは基本パススルーしているため,設定次第ではほとんどVMEXITしなくなります.パフォーマンス的にはそれで良いのですが,BitVisor内部でデバイスを占有して利用している場合や,lwipサーバを実行している場合は,それらの処理はBitVisorのスレッドで実行しているため,VMEXITしないとそれらの処理が一切進まなくなります.外部割り込みでVMEXITさせることにより,BitVisorのスレッドがスケジュールされやすくなります.

また,次で説明する割り込み番号変換にも利用されています.

割り込み対応処理の詳細

current->exint.ack ()の処理の実体はcore/extint_pass.c:exint_pass_ack()であり,以下のように処理されます.

  • core/int.c:do_externalint_enable() => core/int.c:callfunc_and_getint() => core/int_handler.s:int_callfunc()
  • この関数内で,gs:gs_inthandling=1にして一瞬だけ割り込みを有効化 (sti; nop; cli)
  • すると,それまでマスクされてた割り込みが入る
  • 割り込みハンドラ (core/int_handler.s:int_handler)でgs:gs_inthandlingの値をチェックして,その値が1であれば割り込み番号をgs:gs_inthandlingに入れてリターン
    • (そうでなければ(ようするにBitVisor実行中に発生した例外であれば)panic)
  • 割り込みハンドラから戻ってきたら,gs:gs_inthandlingに格納された割り込み番号をチェックして,対応したコールバック関数を実施し,その戻り値を返す.

exint_pass_intr_alloc()exint_pass_intr_register_callback()で特定の割り込みに対してコールバック関数が登録できます.

virtio-netのデバイスドライバは,MSIで割り込みを受けてそれをMSI-Xで通知するということをしていますが,ここの割り込み変換にこのコールバック関数を利用しています.この処理については以前書きました.

また,(割り込み番号変換ではないようですが)NVMeやxHCIのドライバも何かコールバック関数を登録して処理しています.したがってこれらのデバイスドライバを利用する場合はdefconfigでno_intr_intercept=0を設定する必要があります.

参考文献

  • Intel SDM
    • 33.2 INTERRUPT HANDLING IN VMX OPERATION
    • 33.3 EXTERNAL INTERRUPT VIRTUALIZATION

  1. VT-dのPosted Interruptsを利用すると,external-interrupt exitingが1でも特定の割り込みをVMX non-rootのゲストに直接挿入できるみたいです. 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away