Linux Kernel Virtualization

最終更新: 2024-10-12 06:57:35 +0800 +0800
tags: Kernel | Kvm | Linux
原文は英語で書かれています。あなたはAI翻訳版を読んでいます。

KVM は /dev/kvm を通じて Linux のハードウェア仮想化を公開します。KVM はカーネル側の実行機構を提供し、 QEMU などの仮想マシンモニターが完全なゲストマシンを構築・運用します。

責務の分担 #

KVM は Intel VT-x、AMD-V、Arm 仮想化拡張、または対応するホスト機能を使い、可能な場合はゲスト命令を直接実行します。 カーネルコードは vCPU 状態、第二段階のメモリ変換、割り込み配送、タイマー、ゲストとホストの実行切り替えを管理します。 KVM は完全なファームウェアやデバイスモデルを意図的に提供しません。

QEMU はそのマシンモデル、つまりファームウェアインターフェース、PCI トポロジー、ストレージ・ネットワークコントローラー、 表示・入力デバイス、イメージバックエンド、マイグレーション、QMP による管理を提供します。 -accel kvm を指定すると QEMU は高速な CPU 実行を KVM に委譲し、アクセラレーターがなければ TCG 命令トランスレーターを使えます。 QEMU がサポートするオプションはマシンとアーキテクチャに固有です。

ゲストメモリとメモリスロット #

QEMU はまず、匿名メモリ、memfd、ファイル、または huge page をバッキングにしてホスト仮想メモリを予約します。 次に KVM_SET_USER_MEMORY_REGION を使い、そのアドレス空間の一部を VM ファイルディスクリプターへ登録します。 各リージョンは次を記述します。

  • スロット番号とフラグ。
  • ゲスト物理アドレスのベースとサイズ。
  • スロットを裏付けるユーザー空間仮想アドレス。

スロットのサイズを 0 にすると削除されます。スロットはゲスト RAM を割り当てるのではなくマッピングを記述するため、 KVM が使用している間、ユーザー空間はバッキングマッピングを保持する必要があります。任意の dirty-page ロギングは、 マイグレーションとスナップショット調整のために書き込みを記録します。

ゲストメモリ参照には 2 つの変換が関わることがあります。ゲストページテーブルがゲスト仮想アドレスをゲスト物理アドレスへ変換し、 KVM と MMU ハードウェアがそのアドレスを、登録されたユーザー空間マッピングに対応するホスト物理ページへ変換します。 Intel はハードウェアの第二段階を EPT、AMD は NPT と呼びます。KVM は第二段階のマッピング欠落・無効を処理し、 アクセス先が通常の RAM なら QEMU へ exit せずに新しいマッピングを設定できます。 エミュレートされた MMIO 範囲へのアクセスは、代わりに exit またはカーネル内デバイス操作を発生させます。

NUMA 配置、透過または明示的な huge page、スワップ、メモリオーバーコミットは、ゲストから見えるアドレスモデルを変えずにレイテンシーへ影響します。 デバイス割り当ては DMA マッピングを追加するため、VFIO は IOMMU と、無関係なホストデバイスやメモリを公開しない分離境界に依存します。

/dev/kvm のライフサイクル #

安定した KVM ユーザー空間 ABI は、ファイルディスクリプターの型ごとに編成されています。

  1. /dev/kvm を開き、KVM_GET_API_VERSION が KVM_API_VERSION(現在は 12)を返すことを要求します。任意機能はカーネルバージョン比較ではなく KVM_CHECK_EXTENSION で問い合わせます。
  2. システムディスクリプターで KVM_CREATE_VM を発行します。返された VM ディスクリプターは RAM と vCPU を持たずに開始します。
  3. メモリスロットを登録し、必要な場合はアーキテクチャの割り込みコントローラーとその他のカーネル内デバイスを作成します。
  4. VM ディスクリプターで KVM_CREATE_VCPU を発行します。KVM_GET_VCPU_MMAP_SIZE を使い、 vCPU ディスクリプターの共有 struct kvm_run 領域をマップします。
  5. CPU モデルと初期レジスター状態を設定し、vCPU のホストスレッドから KVM_RUN を実行します。

VM とデバイスの ioctl は KVM API が文書化するプロセス・スレッド制約に従う必要があります。 VM のライフタイムは作成プロセスのライフタイムではなくファイルディスクリプターへの参照に従います。 そのため fork で継承された偶発的な参照や dup で作成された参照が VM リソースを保持することがあります。

仮想 CPU、exit、割り込み #

各 vCPU には KVM ディスクリプターがあり、通常は対応する QEMU スレッドがあります。 KVM_RUN はゲストモードに入り、ユーザー空間が処理すべき場合、シグナルがホストスレッドを中断した場合、 または exit 条件が発生した場合に戻ります。QEMU は kvm_run.exit_reason を調べ、ポート I/O、MMIO、シャットダウン、 デバッグイベントなどの exit を処理し、再び KVM_RUN を呼びます。KVM がカーネル内だけで完全に仮想化できる命令では、ユーザー空間 exit は不要です。

割り込みは、ユーザー空間でエミュレートされたコントローラーまたはカーネル内 irqchip から注入できます。 KVM_IRQ_LINE、MSI ルーティング、irqfd はデバイスイベントをゲスト割り込みに接続します。 ioeventfd は、選択したゲスト MMIO または PIO 書き込みで、メインエミュレーションループを往復せず eventfd に通知できます。 KVM はゲスト CPU が受け入れられるとき保留中の割り込みを注入します。

Virtio と exit の削減 #

トラップアンドエミュレートデバイスはハードウェアレジスターインターフェースを再現しますが、 ユーザー空間が処理するすべてのアクセスは KVM 境界を越えます。Virtio は代わりに準仮想化デバイスと共有 virtqueue を定義し、 ゲストはメモリにディスクリプターを置いて処理をまとめた後、ホストへ通知します。 QEMU、カーネル内の vhost バックエンド、または vhost-user プロセスがキューを処理できます。 バッチ処理、割り込み抑制、irqfd/ioeventfd は exit を減らします。一方、デバイスパススルーはソフトウェアデバイスモデルを データパスから除去できますが、より厳しい IOMMU とマイグレーション制約が必要です。

分離と運用上の可観測性 #

/dev/kvm へのアクセスは KVM VM を作成できる者を制御しますが、QEMU プロセスはゲスト制御のデバイス入力とホストファイルも処理します。 信頼できないゲストは専用の ID で実行し、cgroups、名前空間、seccomp、SELinux または AppArmor で QEMU を制限します。 エミュレートするデバイスを最小化し、カーネル、QEMU、ゲストファームウェアを更新し続け、 VFIO IOMMU グループと共有メモリバックエンドを明示的な信頼境界として扱います。 CPU サイドチャネルにはメモリ分離に加えて SMT とスケジューリングポリシーが必要になることもあります。

Basic host checks are:

sh
test -r /dev/kvm && test -w /dev/kvm
ls -l /dev/kvm
lscpu | grep -i virtualization

QMP and management layers such as libvirt report guest state, vCPU time, block I/O, and migration progress. At the host level, perf kvm stat summarizes costly KVM exits, KVM tracepoints under the tracing filesystem expose entry, exit, MMIO, and interrupt events, and /proc/PID/task identifies the QEMU vCPU threads. Debugfs and tracefs require elevated access and can be unavailable when the kernel was built without the relevant options.

権威あるインターフェースは KVM API and the QEMU system-emulation documentation.

コメント

GitHub で閲覧・コメントする(投稿にはログインが必要です)